I\'ve to admit that I don\'t know much about functional programming. I read about it from here and there, and so came to know that in functional programming, a function retu
How can a time function exist in functional programming?
You've almost answered your own question - if it's a time function, then all it needs is the appropriate input. For a language like Standard ML it could look something like:
val time_now : () -> time
fun time_now () = ...
for a suitable definition of time
, of course.
I [...] know that in functional programming, a function returns the same output, for same input, no matter how many times the function is called.
That is possible in Standard ML if you're careful...
For example, consider this:
f(x,y) = x*x + y;
[...] As such, wherever you've written
f(10,4)
, you can replace it with104
, without altering the value of the whole expression. This property is referred to as referential transparency [...]
Aha! This calls for a change of language - let's try...Miranda(R)!
As you've correctly surmised, something like:
unit ::= Unit
time_now :: unit -> time
would only return the same time
value, no matter where it was used - not exactly what we're looking for. We need to use a different input for each call to time_now
:
time_taken x = t1 $seq x $seq t2 $seq (x, tdiff)
where
t1 = time_now u1
t2 = time_now u2
tdiff = t2 $minus_time t1
u1:u2:_ = ... || what goes here?
where:
minus_time :: time -> time -> time
and time
are already defined elsewhere.
(Note: while it is here, seq
isn't actually sequential in all the languages that define it...)
But what about:
times_taken xs = map time_taken xs
All that would happen is each element of the list would be paired with the same time
(difference) value - again, not exactly what was intended.
Favouring simplicity, we reuse the change made to time_now
- use a different input for each call to time_taken
:
times_taken xs = map2 time_taken xs us
where
us = ... || what about here?
That implies:
time_taken x u = t1 $seq x $seq t2 $seq (x, tdiff)
where
t1 = time_now u1
t2 = time_now u2
tdiff = t2 $minus_time t1
u1:u2:_ = ... || what goes here?
which in turn implies:
times_taken xs u = map2 time_taken xs us
where
us = ... || what about here?
so that times_taken
can also be used far and wide.
Now for the enigmatic u1
, u2
and us
- since we've added u
as an extra parameter to time_taken
and times_taken
, let's make use of it with the help of a new definition e.g. parts
:
time_taken x u = t1 $seq x $seq t2 $seq (x, tdiff)
where
t1 = time_now u1
t2 = time_now u2
tdiff = t2 $minus_time t1
u1:u2:_ = parts u
times_taken xs u = map2 time_taken xs us
where
us = parts u
While we're at it, let's also name the type of all these u
-values. Since time_now
uses an outside source of information, what about:
time_now :: outside_information -> time
parts :: outside_information -> [outside_information]
...yeah - on second thought, let's just use the initials:
time_now :: oi -> time
parts :: oi -> [oi]
Much better! This also allows us to provide time_taken
and times_taken
with their own type signatures:
time_taken :: * -> oi -> (*, time)
times_taken :: [*] -> oi -> [(*, time)]
That just leaves parts
and time_now
- how will they use their respective oi
arguments?
Well, you may recall the requirement for each oi
value to be unique for all this to work. But a regular definition:
oi ::= ...
would expose the constructors, which could then be reused at will...
Now consider:
what_time_taken :: * -> oi -> (*, time)
what_time_taken x u = t1 $seq x $seq t2 $seq (x, tdiff)
where
t1 = time_now u1
t2 = time_now u
tdiff = t2 $minus_time t1
u1:u2:_ = parts u
Did you see it?
In the local definition of t2
, time_now
has been applied to u
, instead of (presumably) u2
- u
is being used twice, contrary to the single-use property we're trying to maintain. This is Miranda(R), not Clean, so there are no uniqueness types which could be used to fend off such anomalies...
Those two observations suggest oi
needs to be predefined, like char
or num
- an implementation could then check if any oi
value has already been used and react accordingly e.g. raising a runtime error to stop the offending program (think of how division-by-zero is dealt with now). The simplest way time_now
and parts
can access this runtime check is for both to also be predefined (like ord
or div
) - together with oi
, they form an abstract data type provided by the implementation.
With that out of the way, let's bring everything together:
|| oi ADT
parts :: oi -> [oi]
time_now :: oi -> Time
minus_time :: time -> time -> time || defined elsewhere
time_taken :: * -> oi -> (*, time)
time_taken x u = t1 $seq x $seq t2 $seq (x, tdiff)
where
t1 = time_now u1
t2 = time_now u2
tdiff = t2 $minus_time t1
u1:u2:_ = parts u
times_taken :: [*] -> oi -> [(*, time)]
times_taken xs u = map2 time_taken xs us
where
us = parts u
By now, you've probably already noticed how the use of parts
, u
, etc form a tree embedded across times_taken
and times_taken
, with its leaves in the applications of time_now
. This suggests the existence of a single ancestral oi
value e.g:
start_here :: oi -> unit
We already know having something like:
start_up :: oi
is useless because it will always have the same value...wait a moment - we're trying to bring a new oi
value to start_now
? That's so first-order!
In Miranda(R), functions can be used like any other value of e.g. bool
, char
, or num
i.e. functions are first-class values. Let's just bring start_here
to a newly-made oi
value inside the implementation, since it already chooses how to process its input based on type:
using quotes for printing char
values;
using the decimal point (if needed) for printing num
values.
We just need to extend it to cater for functions using oi
values; the implementation can then:
evaluate the input expression if it isn't already a function;
generate a new oi
value;
apply the function to the oi
value to form a new input expression;
which is then sent back for more processing, again based on type.
To obtain a new oi
value, the implementation can use a technique similar to what parts
uses to generate new oi
values.
To conclude:
How can a time function exist in functional programming?
By always being applied to a unique input value wherever it's called.
I am surprised that none of the answers or comments mention coalgebras or coinduction. Usually, coinduction is mentioned when reasoning about infinite data structures, but it is also applicable to an endless stream of observations, such as a time register on a CPU. A coalgebra models hidden state; and coinduction models observing that state. (Normal induction models constructing state.)
This is a hot topic in Reactive Functional Programming. If you're interested in this sort of stuff, read this: http://digitalcommons.ohsu.edu/csetech/91/ (28 pp.)
Yes! You are correct! Now() or CurrentTime() or any method signature of such flavour is not exhibiting referential transparency in one way. But by instruction to the compiler it is parameterized by a system clock input.
By output, Now() might look like not following referential transparency. But actual behaviour of the system clock and the function on top of it is adheres to referential transparency.
Yes and no.
Different functional programming languages solve them differently.
In Haskell (a very pure one) all this stuff has to happen in something called the I/O Monad - see here.
You can think of it as getting another input (and output) into your function (the world-state) or easier as a place where "impureness" like getting the changing time happens.
Other languages like F# just have some impureness built in and so you can have a function that returns different values for the same input - just like normal imperative languages.
As Jeffrey Burka mentioned in his comment: Here is the nice introduction to the I/O Monad straight from the Haskell wiki.
Yes, it's possible for a pure function to return the time, if it's given that time as a parameter. Different time argument, different time result. Then form other functions of time as well and combine them with a simple vocabulary of function(-of-time)-transforming (higher-order) functions. Since the approach is stateless, time here can be continuous (resolution-independent) rather than discrete, greatly boosting modularity. This intuition is the basis of Functional Reactive Programming (FRP).
Your question conflates two related measures of a computer language: functional/imperative and pure/impure.
A functional language defines relationships between inputs and outputs of functions, and an imperative language describes specific operations in a specific order to perform.
A pure language does not create or depend on side effects, and an impure language uses them throughout.
One-hundred percent pure programs are basically useless. They may perform an interesting calculation, but because they cannot have side effects they have no input or output so you would never know what they calculated.
To be useful at all, a program has to be at least a smidge impure. One way to make a pure program useful is to put it inside a thin impure wrapper. Like this untested Haskell program:
-- this is a pure function, written in functional style.
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- This is an impure wrapper around the pure function, written in imperative style
-- It depends on inputs and produces outputs.
main = do
putStrLn "Please enter the input parameter"
inputStr <- readLine
putStrLn "Starting time:"
getCurrentTime >>= print
let inputInt = read inputStr -- this line is pure
let result = fib inputInt -- this is also pure
putStrLn "Result:"
print result
putStrLn "Ending time:"
getCurrentTime >>= print