development

함수형 프로그래밍에 시간 함수가 어떻게 존재할 수 있습니까?

big-blog 2020. 10. 2. 22:53
반응형

함수형 프로그래밍에 시간 함수가 어떻게 존재할 수 있습니까?


함수형 프로그래밍에 대해 잘 모른다는 사실을 인정해야합니다. 나는 여기 저기에서 그것에 대해 읽었고 함수형 프로그래밍에서 함수가 몇 번 호출 되더라도 동일한 입력에 대해 동일한 출력을 반환한다는 것을 알게되었습니다. 함수 표현식에 포함 된 입력 매개 변수의 동일한 값에 대해 동일한 출력으로 평가되는 수학 함수와 정확히 같습니다.

예를 들어 다음을 고려하십시오.

f(x,y) = x*x + y; // It is a mathematical function

몇 번 사용하더라도 f(10,4)그 가치는 항상 될 것 104입니다. 따라서를 작성 f(10,4)하는 위치 104에 관계없이 전체 표현식의 값을 변경하지 않고로 바꿀 수 있습니다 . 이 속성을 식의 참조 투명성 이라고합니다 .

Wikipedia가 말했듯이 ( link ),

반대로 함수 코드에서 함수의 출력 값은 함수에 입력 된 인수에만 의존하므로 인수 x에 대해 동일한 값으로 함수 f를 두 번 호출하면 두 번 모두 동일한 결과 f (x)가 생성됩니다.

함수형 프로그래밍에 시간 함수 ( 현재 시간 을 반환 )가 존재할 수 있습니까?

  • 그렇다면 어떻게 존재할 수 있습니까? 함수형 프로그래밍의 원칙을 위반하지 않습니까? 특히 함수형 프로그래밍의 속성 중 하나 인 참조 투명성위반 합니다 (올바르게 이해한다면).

  • 아니면 그렇지 않다면 함수형 프로그래밍에서 현재 시간을 어떻게 알 수 있습니까?


이를 설명하는 또 다른 방법은 다음과 같습니다. 어떤 함수도 현재 시간을 가져올 수 없지만 (계속 변경되므로) 작업 은 현재 시간을 가져올 수 있습니다. 현재 시간을 가져 오는 동작getClockTime 을 나타내는 상수 (또는 원하는 경우 널 함수) 라고 가정 해 보겠습니다 . 동작 은 언제 사용 되든 매번 동일하므로 실제 상수입니다.

마찬가지로 print시간 표현을 사용하여 콘솔에 인쇄하는 함수 라고 가정 해 봅시다 . 함수 호출은 순수한 기능적 언어에서 부작용을 가질 수 없기 때문에 대신 타임 스탬프 를 받아 콘솔에 인쇄 하는 작업반환하는 함수라고 상상합니다 . 다시 말하지만, 이것은 실제 함수입니다. 동일한 타임 스탬프를 제공 하면 매번 인쇄 하는 동일한 동작반환하기 때문입니다 .

이제 콘솔에 현재 시간을 어떻게 인쇄 할 수 있습니까? 글쎄, 당신은 두 행동을 결합해야합니다. 그럼 어떻게 할 수 있습니까? 우리는 전달할 수 없습니다 getClockTimeprint인쇄가 타임 스탬프 아닌 행동을 기대하고 있기 때문에. 그러나 우리 두 개의 동작 >>=결합 하는 연산자가 있다고 상상할 수 있습니다 . 하나는 타임 스탬프를 가져오고 하나는 인수로 받아 출력합니다. 이것을 앞서 언급 한 액션에 적용하면 결과는 ... tadaaa ... 현재 시간을 가져 와서 인쇄하는 새로운 액션입니다. 그리고 이것은 우연히 하스켈에서 정확히 수행되는 방식입니다.

Prelude> System.Time.getClockTime >>= print
Fri Sep  2 01:13:23 東京 (標準時) 2011

따라서 개념적으로 다음과 같이 볼 수 있습니다. 순수한 기능적 프로그램은 I / O를 수행하지 않고 런타임 시스템이 실행 하는 액션을 정의합니다 . 동작은 매번 동일하지만 그것의 실행 결과가 실행되었을 때의 상황에 따라 달라진다.

이것이 다른 설명보다 더 명확한 것인지는 모르겠지만, 때때로 이런 식으로 생각하는 데 도움이됩니다.


예, 아니오.

다른 함수형 프로그래밍 언어는 그것들을 다르게 해결합니다.

하스켈 (매우 순수 하나)에서 모든 물건을 호출 뭔가 발생하는 I / O 모나드 참조 - 여기 .

함수 (세계 상태)에 또 다른 입력 (및 출력)을 가져 오는 것으로 생각하거나 시간 변화와 같은 "불순함"이 발생하는 장소로 더 쉽게 생각할 수 있습니다.

F #과 같은 다른 언어에는 약간의 불순함이 내장되어 있으므로 일반 명령형 언어 와 마찬가지로 동일한 입력에 대해 다른 값을 반환하는 함수를 사용할 수 있습니다.

Jeffrey Burka가 그의 의견에서 언급했듯이 : Haskell 위키에서 직접 I / O Monad에 대한 멋진 소개가 있습니다.


Haskell에서는 부작용을 처리하기 위해 monad 라는 구조를 사용합니다 . 모나드는 기본적으로 값을 컨테이너로 캡슐화하고 값에서 컨테이너 내부의 값으로 함수를 연결하는 일부 함수를 가지고 있음을 의미합니다. 컨테이너에 다음 유형이있는 경우 :

data IO a = IO (RealWorld -> (a,RealWorld))

IO 작업을 안전하게 구현할 수 있습니다. 이 유형은 다음을 의미합니다. 유형의 조치는 유형 IO의 토큰을 취하고 RealWorld결과와 함께 새 토큰을 리턴하는 함수입니다.

이이면의 아이디어는 각 IO 작업이 마법 토큰으로 표현되는 외부 상태를 변경한다는 것 RealWorld입니다. 모나드를 사용하면 실제 세계를 함께 변형시키는 여러 함수를 연결할 수 있습니다. 모나드의 가장 중요한 기능은 bind>>= 로 발음됩니다 .

(>>=) :: IO a -> (a -> IO b) -> IO b

>>=하나의 동작과이 동작의 결과를 취하는 함수를 취하고 이것으로부터 새로운 동작을 생성합니다. 반환 유형은 새 작업입니다. 예를 들어, now :: IO String현재 시간을 나타내는 문자열을 반환하는 함수가 있다고 가정 해 봅시다 . 우리는 그것을 putStrLn출력 하는 함수 연결할 수 있습니다 :

now >>= putStrLn

또는 do명령형 프로그래머에게 더 친숙한 -Notation으로 작성되었습니다 .

do currTime <- now
   putStrLn currTime

외부 세계에 대한 돌연변이와 정보를 RealWorld토큰 에 매핑하기 때문에이 모든 것이 순수합니다 . 따라서이 작업을 실행할 때마다 물론 다른 출력이 표시되지만 입력은 동일하지 않습니다 RealWorld. 토큰이 다릅니다.


대부분의 함수형 프로그래밍 언어는 순수하지 않습니다. 즉, 함수가 값에만 의존하지 않도록합니다. 이러한 언어에서는 현재 시간을 반환하는 함수를 갖는 것이 완벽하게 가능합니다. 이 질문에 태그를 지정한 언어에서 ScalaF # ( 대부분의 다른 ML 변형 )에 적용됩니다.

In languages like Haskell and Clean, which are pure, the situation is different. In Haskell the current time would not be available through a function, but a so-called IO action, which is Haskell's way of encapsulating side effects.

In Clean it would be a function, but the function would take a world value as its argument and return a fresh world value (in addition to the current time) as its result. The type system would make sure that each world value can be used only once (and each function which consumes a world value would produces a new one). This way the time function would have to be called with a different argument each time and thus would be allowed to return a different time each time.


"Current time" is not a function. It is a parameter. If your code depends on current time, it means your code is parameterized by time.


It can absolutely be done in a purely functional way. There are several ways to do it, but the simplest is to have the time function return not just the time but also the function you must call to get the next time measurement.

In C# you could implement it like this:

// Exposes mutable time as immutable time (poorly, to illustrate by example)
// Although the insides are mutable, the exposed surface is immutable.
public class ClockStamp {
    public static readonly ClockStamp ProgramStartTime = new ClockStamp();
    public readonly DateTime Time;
    private ClockStamp _next;

    private ClockStamp() {
        this.Time = DateTime.Now;
    }
    public ClockStamp NextMeasurement() {
        if (this._next == null) this._next = new ClockStamp();
        return this._next;
    }
}

(Keep in mind that this is an example meant to be simple, not practical. In particular, the list nodes can't be garbage collected because they are rooted by ProgramStartTime.)

This 'ClockStamp' class acts like an immutable linked list, but really the nodes are generated on demand so they can contain the 'current' time. Any function that wants to measure the time should have a 'clockStamp' parameter and must also return its last time measurement in its result (so the caller doesn't see old measurements), like this:

// Immutable. A result accompanied by a clockstamp
public struct TimeStampedValue<T> {
    public readonly ClockStamp Time;
    public readonly T Value;
    public TimeStampedValue(ClockStamp time, T value) {
        this.Time = time;
        this.Value = value;
    }
}

// Times an empty loop.
public static TimeStampedValue<TimeSpan> TimeALoop(ClockStamp lastMeasurement) {
    var start = lastMeasurement.NextMeasurement();
    for (var i = 0; i < 10000000; i++) {
    }
    var end = start.NextMeasurement();
    var duration = end.Time - start.Time;
    return new TimeStampedValue<TimeSpan>(end, duration);
}

public static void Main(String[] args) {
    var clock = ClockStamp.ProgramStartTime;
    var r = TimeALoop(clock);
    var duration = r.Value; //the result
    clock = r.Time; //must now use returned clock, to avoid seeing old measurements
}

Of course, it's a bit inconvenient to have to pass that last measurement in and out, in and out, in and out. There are many ways to hide the boilerplate, especially at the language design level. I think Haskell uses this sort of trick and then hides the ugly parts by using monads.


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, 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).


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, a getting time function can exist in functional programming using a slightly modified version on functional programming known as impure functional programming (the default or the main one is pure functional programming).

In case of getting the time (or reading file, or launching missile) the code needs to interact with the outer world to get the job done and this outer world is not based on the pure foundations of functional programming. To allow a pure functional programming world to interact with this impure outside world, people have introduced impure functional programming. After all, software which doesn't interact with the outside world isn't any useful other than doing some mathematical computations.

Few functional programming programming languages have this impurity feature inbuilt in them such that it is not easy to separate out which code is impure and which is pure (like F#, etc.) and some functional programming languages make sure that when you do some impure stuff that code is clearly stand out as compared to pure code, like Haskell.

Another interesting way to see this would be that your get time function in functional programming would take a "world" object which has the current state of the world like time, number of people living in the world, etc. Then getting time from which world object would be always pure i.e you pass in the same world state you will always get the same time.


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

You're broaching a very important subject in functional programming, that is, performing I/O. The way many pure languages go about it is by using embedded domain-specific languages, e.g., a sublanguage whose task it is to encode actions, which can have results.

The Haskell runtime for example expects me to define an action called main that is composed of all actions that make up my program. The runtime then executes this action. Most of the time, in doing so it executes pure code. From time to time the runtime will use the computed data to perform I/O and feeds back data back into pure code.

You might complain that this sounds like cheating, and in a way it is: by defining actions and expecting the runtime to execute them, the programmer can do everything a normal program can do. But Haskell's strong type system creates a strong barrier between pure and "impure" parts of the program: you cannot simply add, say, two seconds to the current CPU time, and print it, you have to define an action that results in the current CPU time, and pass the result on to another action that adds two seconds and prints the result. Writing too much of a program is considered bad style though, because it makes it hard to infer which effects are caused, compared to Haskell types that tell us everything we can know about what a value is.

Example: clock_t c = time(NULL); printf("%d\n", c + 2); in C, vs. main = getCPUTime >>= \c -> print (c + 2*1000*1000*1000*1000) in Haskell. The operator >>= is used to compose actions, passing the result of the first to a function resulting in the second action. This looking quite arcane, Haskell compilers support syntactic sugar that allows us to write the latter code as follows:

type Clock = Integer -- To make it more similar to the C code

-- An action that returns nothing, but might do something
main :: IO ()
main = do
    -- An action that returns an Integer, which we view as CPU Clock values
    c <- getCPUTime :: IO Clock
    -- An action that prints data, but returns nothing
    print (c + 2*1000*1000*1000*1000) :: IO ()

The latter looks quite imperative, doesn't it?


If yes, then how can it exist? Does it not violate the principle of functional programming? It particularly violates referential transparency

It does not exist in a purely functional sense.

Or if no, then how can one know the current time in functional programming?

It may first be useful to know how a time is retrieved on a computer. Essentially there is onboard circuitry that keeps track of the time (which is the reason a computer would usually need a small cell battery). Then there might be some internal process that sets the value of time at a certain memory register. Which essentially boils down to a value that can be retrieved by the CPU.


For Haskell, there is a concept of an 'IO action' which represents a type that can be made to carry out some IO process. So instead of referencing a time value we reference a IO Time value. All this would be purely functional. We aren't referencing time but something along the lines of 'read the value of the time register'.

When we actually execute the Haskell program, the IO action would actually take place.

참고URL : https://stackoverflow.com/questions/7267760/how-can-a-time-function-exist-in-functional-programming

반응형