時間関数は関数型プログラミングにどのように存在できますか?


646

私は関数型プログラミングについてあまり知らないことを認めざるを得ません。私はそれをあちこちから読んだので、関数型プログラミングでは、関数が何度呼び出されても、同じ入力に対して同じ関数が同じ出力を返すことがわかりました。これは、関数式に含まれる入力パラメーターの同じ値に対して同じ出力に評価される数学関数とまったく同じです。

たとえば、次のことを考慮してください。

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

何回使ってf(10,4)もその価値は常にあります104。そのため、記述した場所はどこでも、式全体の値を変更せずに、でf(10,4)置き換えることができます104。このプロパティは、式の参照透過性と呼ばれます。

ウィキペディアが言うように(リンク)、

逆に、関数コードでは、関数の出力値は関数に入力された引数にのみ依存するため、引数xに対して同じ値で関数fを2回呼び出すと、同じ結果f(x)が両方生成されます。

現在の時間を返す)時間関数は関数型プログラミングに存在できますか?

  • はいの場合、どのようにして存在できますか?関数型プログラミングの原則に違反していませんか?これは、関数型プログラミングの特性の1つである参照の透過性に特に違反しています(正しく理解していれば)。

  • または、いいえの場合、関数型プログラミングの現在の時刻をどのように知ることができますか?


15
ほとんど(またはすべて)の関数型言語はそれほど厳密ではなく、関数型プログラミングと命令型プログラミングを組み合わせていると思います。少なくとも、これはF#からの私の印象です。
Alex F

13
@アダム:発信者は、そもそもどうやって現在時刻を知るのでしょうか
Nawaz、2011

29
@Adam:実際には、純粋に関数型の言語では違法です(例:不可能)。
sepp2k、2011

47
@アダム:かなり。純粋な汎用言語は、通常、参照の透過性を損なうことなく「世界の状態」(つまり、現在の時刻、ディレクトリ内のファイルなど)に到達するための機能を提供します。HaskellではIOモナドで、Cleanではワールドタイプです。したがって、これらの言語では、現在の時刻を必要とする関数は、それを引数として取るか、実際の結果(Haskell)ではなくIOアクションを返すか、世界の状態を引数(Clean)にする必要があります。
sepp2k

12
FPについて考えると、忘れがちです。コンピュータは、変更可能な状態の大きなかたまりです。FPはそれを変更せず、単に隠すだけです。
ダニエル

回答:


176

それを説明するもう1つの方法は次のとおりです。関数は現在の時刻を取得できません(変化し続けるため)。ただし、アクションは現在の時刻を取得できます。レッツ・発言getClockTimeを表す(あなたが好きならば、または引数なしの関数で)一定であるアクション現在の時間を取得するに。このアクションはいつ使用しても同じであり、実際の定数です。

同様に、printいくつかの時間表現を取り、それをコンソールに出力する関数であるとしましょう。関数呼び出しは純粋な関数型言語では副作用がないため、タイムスタンプを取得してコンソールに出力するアクションを返す関数であると想像します。あなたはそれを同じタイムスタンプを与えた場合、それは同じ戻りますので繰り返しますが、これは本当の関数は、あるアクションそれを毎回印刷します。

では、現在の時刻をコンソールに出力するにはどうすればよいでしょうか。さて、あなたは2つのアクションを組み合わせる必要があります。それでは、どうすればよいでしょうか。printはアクションではなくタイムスタンプを期待するgetClockTimeためprint、単にに渡すことはできません。しかし 2つのアクション>>=組み合わせた演算子があると想像できます。1つはタイムスタンプを取得し、もう1つは引数として1つ取得して出力します。これを前述のアクションに適用すると、結果は... tadaaa ...現在の時刻を取得して出力する新しいアクションです。ちなみに、これはHaskellでの方法とまったく同じです。

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

したがって、概念的には、次のように表示できます。純粋な関数型プログラムはI / Oを実行せず、ランタイムシステムが実行するアクションを定義します。アクションは毎回同じであるが、それを実行した結果は、それが実行されたときの状況に依存します。

これが他の説明よりも明確であったかどうかはわかりませんが、このように考えると役立つことがあります。


33
それは私には納得できません。getClockTime関数の代わりにアクションを呼び出すと便利です。ええと、それを呼び出す場合は、すべての関数アクションを呼び出すと、命令型プログラミングでさえ関数型プログラミングになります。あるいは、それをアクション型プログラミングと呼んでもよいでしょう。
Nawaz、2011

92
@Nawaz:ここで注意すべき重要な点は、関数内からアクションを実行できないことです。アクションと機能を組み合わせて新しいアクションを作成することしかできません。アクションを実行する唯一の方法は、アクションにアクションを組み込むことmainです。これにより、純粋な機能コードを命令コードから分離でき、この分離は型システムによって強制されます。アクションをファーストクラスのオブジェクトとして扱うと、アクションを渡し、独自の「制御構造」を構築することもできます。
hammar 2011

36
Haskellのすべてが関数であるわけではありません-それはまったくナンセンスです。関数とは、型にaが含まれるものです。これが->、標準が用語を定義する方法であり、これが実際にHaskellのコンテキストで唯一の賢明な定義です。つまり、型が関数でIO Whateverないもの。
sepp2k

9
@ sepp2kでは、myList :: [a-> b]は関数ですか?;)
fuz

8
@ThomasEding私は本当にパーティーに遅れますが、私はこれを明確にしたいだけです:putStrLnアクションではありません–アクションを返す関数です。アクションgetLine含む変数です。アクションは値であり、変数と関数は、それらのアクションに与える「コンテナ」/「ラベル」です。
kqr 2013

356

はいといいえ。

異なる関数型プログラミング言語は、それらを異なる方法で解決します。

Haskell(非常に純粋なもの)では、これらすべてはI / Oモナドと呼ばれるもので発生する必要がありますここを参照してください

それは、関数(世界の状態)への別の入力(および出力)を取得することと考えることができます。

F#のような他の言語には、不純さが組み込まれているだけなので、同じ入力に対して異なる値を返す関数を持つことができます- 通常の命令型言語のように。

Jeffrey Burkaが彼のコメントで述べたように:Haskell wikiから直接I / Oモナドへの素晴らしい紹介があります。


223
HaskellのIOモナドについて理解するための重要なことは、この問題を回避するのは単なるハックではないということです。モナドは、いくつかのコンテキストで一連のアクションを定義するという問題に対する一般的な解決策です。考えられるコンテキストの1つは、IOモナドがある実世界です。もう1つのコンテキストは、STMモナドがあるアトミックトランザクション内です。さらにもう1つのコンテキストは、手続き型アルゴリズム(Knuthシャッフルなど)を純粋な関数として実装することです。そのために、STモナドがあります。また、独自のモナドを定義することもできます。モナドは一種のオーバーロード可能なセミコロンです。
ポールジョンソン

2
現在の時間を取得するようなものを「関数」と呼ぶのではなく、「手続き」と呼ぶのが便利だと思います(ただし、Haskellソリューションはこれの例外です)。
singpolyma 2012年

Haskellの観点からは、古典的な「手順」( '...->()'のような型を持つもの)はいくらか簡単です。
Carsten、

3
典型的なHaskellの用語は「アクション」です。
Sebastian Redl、2015年

6
「モナドは一種のオーバーロード可能なセミコロンです。」+1
user2805751 2017

147

Haskellではモナドと呼ばれる構造を使用して副作用を処理します。モナドとは基本的に、値をコンテナーにカプセル化し、関数を値からコンテナー内の値にチェーンするいくつかの関数があることを意味します。コンテナのタイプが次の場合:

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

安全にIOアクションを実装できます。このタイプの意味:タイプのアクションはIO関数であり、タイプのトークンを取りRealWorld、結果とともに新しいトークンを返します。

この背後にある考え方は、各IOアクションが魔法のトークンで表される外部状態を変化させるというものRealWorldです。モナドを使用すると、実際の世界を一緒に変化させる複数の関数を連鎖させることができます。モナドの最も重要な関数は>>=bindと発音されます:

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

>>=1つのアクションと、このアクションの結果を取り、これから新しいアクションを作成する関数を実行します。戻り値の型は新しいアクションです。たとえばnow :: IO String、現在の時刻を表す文字列を返す関数があるとします。それを出力する関数putStrLnと連鎖させることができます:

now >>= putStrLn

またはdo-Notationで書かれ、命令型プログラマにとってより馴染みのあるものです。

do currTime <- now
   putStrLn currTime

突然変異と外部の世界に関する情報をRealWorldトークンにマッピングするため、これはすべて純粋です。したがって、このアクションを実行するたびに、当然異なる出力が得られますが、入力は同じではなく、RealWorldトークンが異なります。


3
-1:RealWorld煙幕に不満があります。しかし、最も重要なことは、この目的のオブジェクトがチェーンで渡される方法です。欠けている部分は、それが始まるところ、現実世界へのソースまたは接続があるところです-IOモナドで実行されるmain関数から始まります。
u0b34a0f6ae

2
@ kaizer.se RealWorldプログラムの起動時にプログラムに渡されるグローバルオブジェクトと考えることができます。
fuz

6
基本的に、main関数はRealWorld引数を取ります。のみ、実行時にそれが渡される。
ルイ・ワッサーマン

13
ご覧のRealWorldとおり、彼らがを隠し、それをのように変更するためのちっぽけな機能しか提供していない理由putStrLnは、一部のHaskellプログラマーがRealWorldプログラムのいずれかを変更しないため、Haskell Curryの住所と生年月日が隣の隣人になるようなものです成長する(これは、Haskellプログラミング言語を傷つけるような方法で時空間の連続体に損傷を与える可能性があります。)
PyRulez

2
RealWorld -> (a, RealWorld) 実世界が常に関数(または現在のプロセス)の外にある宇宙の他の部分によって変更される可能性があることを念頭に置いている限り、並行性の下でもメタファーとして分解されません。したがって、(a)メタファーは分解されず、(b)RealWorld型として持つ値が関数に渡されるたびに、実際の世界が変化するため、関数を再評価する必要があります(これは、@ fuzが説明したようにモデル化されており、現実の世界と対話するたびに異なる「トークン値」を返します)。
Qqwy

73

ほとんどの関数型プログラミング言語は純粋ではありません。つまり、関数は値に依存するだけではありません。これらの言語では、現在の時刻を返す関数を持つことは完全に可能です。この質問にタグを付けた言語から、これはScalaF#(およびMLの他のほとんどのバリアント)に適用されます。

HaskellCleanなどの純粋な言語では、状況が異なります。Haskellでは、現在の時間は関数では利用できませんが、いわゆるIOアクションと呼ばれます。これは、Haskellが副作用をカプセル化する方法です。

Cleanでは関数ですが、関数は引数としてワールド値を取り、その結果として(現在の時間に加えて)新しいワールド値を返します。型システムは、各ワールド値が1回だけ使用できることを確認します(そして、ワールド値を使用する各関数は新しい値を生成します)。このように、時間関数は毎回異なる引数で呼び出される必要があるため、毎回異なる時間を返すことができます。


2
これにより、HaskellとCleanが異なることを行うかのように聞こえます。私が理解していることから、Haskellがこれを実現するためのより良い構文(?)を提供しているというだけで、彼らは同じことをします。
Konrad Rudolph

27
@Konrad:どちらも型システムの機能を使用して副作用を抽象化するという意味で同じことを行いますが、それはそれだけです。IOモナドをワールドタイプの観点から説明することは非常に適切ですが、Haskell標準は実際にはワールドタイプを定義しておらず、HaskellでタイプWorldの値を取得することは実際には不可能であることに注意してください。きれいに必要)。さらに、Haskellには型システム機能としての一意性の型指定がないため、Worldへのアクセスを許可したとしても、Cleanのように純粋に使用することはできません。
sepp2k 2011

51

「現在時刻」は機能ではありません。パラメータです。コードが現在の時刻に依存している場合は、コードが時間でパラメーター化されていることを意味します。


22

それは純粋に機能的な方法で絶対に行うことができます。これを行う方法はいくつかありますが、最も簡単なのは、時間関数が時間だけでなく、次の時間測定を取得するために呼び出す必要ある関数も返すようにすることです。

C#では、次のように実装できます。

// 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;
    }
}

(これは単純で実用的ではないことを意図した例であることを覚えておいてください。特に、リストノードはProgramStartTimeをルートとしているため、ガベージコレクションできません。)

この「ClockStamp」クラスは不変のリンクリストのように機能しますが、実際にはノードはオンデマンドで生成されるため、「現在」の時間を含めることができます。次のように、時間を測定したい関数はすべて 'clockStamp'パラメータを持ち、その最後の時間測定値を結果に返す必要があります(したがって、呼び出し元には古い測定値が表示されません)。

// 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
}

もちろん、その最後の測定値を出し入れ、出し入れ、出し入れしなければならないのは少し不便です。ボイラープレートを非表示にする方法はたくさんありますが、特に言語設計レベルではそうです。Haskellはこの種のトリックを使用し、モナドを使用して醜い部分を隠していると思います。


おもしろいですがi++、forループは参照的に透過的ではありません;)
snim2

@ snim2私は完璧ではありません。:Pダーティな変更可能性が結果の​​参照透明度に影響を与えないという事実で慰めを取りなさい。同じ 'lastMeasurement'を2回渡すと、次の測定が古くなり、同じ結果が返されます。
Craig Gidney

@Strilancこれをありがとう。私は命令型コードで考えるので、このように説明された関数の概念を見るのは興味深いです。次に、この自然で構文的にクリーンな言語を想像できます。
WW。

実際にはC#でもモナドのように移動できるため、タイムスタンプが明示的に渡されるのを回避できます。のようなものが必要ですstruct TimeKleisli<Arg, Res> { private delegate Res(TimeStampedValue<Arg>); }。しかし、これを使用したコードは、do構文を使用したHaskellほど美しくありません。
leftaroundabout

@leftaroundaboutでは、バインド関数をと呼ばれるメソッドとして実装することで、C#にモナドがあるふりをすることがSelectManyできます。これにより、クエリ内包構文が有効になります。ただし、モナドに対して多態的にプログラムすることはまだできないため、弱い型システムに対する困難な戦いになります:(
sara

16

答えやコメントのいずれにも、代数や共和についての記述がないことに驚いています。通常、無限結合は、無限のデータ構造について推論するときに言及されますが、CPUのタイムレジスタなど、観測の無限ストリームにも適用できます。共代数は隠れた状態をモデル化します。そしてその状態を観察する連成モデル。(状態を構築する通常の誘導モデル。)

これは、リアクティブ関数型プログラミングのホットトピックです。この種のものに興味があるなら、これを読んでください:http : //digitalcommons.ohsu.edu/csetech/91/(28 pp。)


3
そして、それはこの質問とどのように関連していますか?
Nawaz 2014

5
あなたの質問は、純粋に機能的な方法、たとえば現在のシステムクロックを返す関数で時間依存動作をモデル化することに関するものでした。IOモナドに相当するものをすべての関数とその依存関係ツリーに通して、その状態にアクセスできます。または、建設的な規則ではなく観察規則を定義することで状態をモデル化できます。このため、関数型プログラミングで複雑な状態を帰納的にモデル化するのは非常に不自然に見えます。なぜなら、隠された状態は実際には共帰的な性質だからです。
ジェフリーアギレラ2014

素晴らしい情報源!最近何かありますか?JSコミュニティは、ストリームデータの抽象化にまだ苦労しているようです。
ドミトリ・ザイツェフ

12

はい、純粋な関数が時間をパラメーターとして指定した場合、その時間を返すことができます。異なる時間の引数、異なる時間の結果。次に、時間の他の関数も形成し、それらを関数(-of-time)変換(高次)関数の単純な語彙と組み合わせます。アプローチはステートレスであるため、ここでの時間は離散ではなく連続(解像度に依存しない)になり、モジュール性が大幅に向上します。この直感は、Functional Reactive Programming(FRP)の基礎です。


11

はい!あなたは正しいです!Now()、CurrentTime()、またはそのようなフレーバーのメソッドシグネチャは、ある意味で参照透過性を示していません。しかし、コンパイラーへの命令によって、システムクロック入力によってパラメーター化されます。

出力では、Now()は参照透過性に従っていないように見える場合があります。ただし、システムクロックとその上にある関数の実際の動作は、参照透過性に準拠しています。


11

はい、取得時間関数は、純粋でない関数型プログラミングと呼ばれる関数型プログラミングのわずかに変更されたバージョンを使用して、関数型プログラミングに存在できます(デフォルトまたはメインの関数型プログラミングは純粋な関数型プログラミングです)。

時間を取得する(またはファイルを読み取る、またはミサイルを起動する)場合、コードは外界と相互作用して仕事を完了する必要があり、この外界は関数型プログラミングの純粋な基盤に基づいていません。純粋な関数型プログラミングの世界がこの不純な外界と相互作用できるようにするために、人々は不純な関数型プログラミングを導入しました。結局のところ、外界と相互作用しないソフトウェアは、いくつかの数学的計算を行うこと以外には何の役にも立ちません。

一部の関数型プログラミング言語には、この不純な機能が組み込まれているため、不純なコードと純粋なコード(F#など)を簡単に区別することは容易ではなく、一部の関数型プログラミング言語では、不純なことを行うときにそのコードは、Haskellのような純粋なコードと比較して明らかに目立ちます。

これを確認するもう1つの興味深い方法は、関数型プログラミングでのget time関数が、時間、世界に住んでいる人の数など、世界の現在の状態を持つ「世界」オブジェクトを取得することです。オブジェクトは常に純粋です。つまり、同じ世界の状態で渡すと、常に同じ時刻になります。


1
「結局のところ、外界と相互作用しないソフトウェアは、いくつかの数学的計算を行うこと以外には何の役にも立ちません。」私が理解している限り、この場合でも、計算への入力はプログラムでハードコード化されるため、あまり役に立ちません。ファイルまたはターミナルから数学計算への入力データを読み取りたいと思ったらすぐに、不純なコードが必要になります。
Giorgio

1
@Ankur:それはまったく同じです。プログラムがそれ自体以外のものと対話している場合(たとえば、キーボードを介した世界など)、それはまだ不純です。
ID

1
@Ankur:はい、あなたは正しいと思います!コマンドラインで大きな入力データを渡すことはあまり実用的ではないかもしれませんが、これは純粋な方法です。
Giorgio

2
世界に住んでいる人の数を含む「世界オブジェクト」を持つことは、実行しているコンピュータをほぼ全知的なレベルに引き上げます。通常の場合、HDにあるファイルの数や現在のユーザーのホームディレクトリなどが含まれていると思います。
ziggystar 2011

4
@ziggystar-「ワールドオブジェクト」には実際には何も含まれていません。これは、プログラム外のワールドの変化する状態のプロキシにすぎません。その唯一の目的は、型システムが識別できるように、変更可能な状態を明示的にマークすることです。
Kris Nuttycombe 2011

7

あなたの質問は、コンピューター言語の2つの関連する測定値、関数型/命令型と純粋/不純型をまとめたものです。

関数型言語は、関数の入力と出力の間の関係を定義し、命令型言語は、実行する特定の順序で特定の操作を記述します。

純粋な言語は副作用を作成したり、それに依存したりすることはなく、不純な言語が副作用を使用しています。

100%純粋なプログラムは基本的に役に立たない。興味深い計算を実行する可能性がありますが、副作用がないため、入力または出力がないため、何が計算されたのかはわかりません。

まったく有用であるためには、プログラムは少なくとも少しばかり不純である必要があります。純粋なプログラムを有用にする1つの方法は、それを薄い不純なラッパーの中に置くことです。この未テストのHaskellプログラムのように:

-- 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

4
時間を取得するという特定の問題に対処でき、IO値と結果を純粋にどの程度検討するかについて少し説明していただければ助かります。
AndrewC 2012

実際、100%純粋なプログラムでもCPUを加熱しますが、これは副作用です。
イェルクWミッターク

3

関数型プログラミング、つまりI / Oを実行する上で非常に重要なテーマを説明します。多くの純粋な言語がそれを実現する方法は、埋め込みドメイン固有言語を使用することです。たとえば、アクションをエンコードすることをタスクとするサブ言語です。の結果を持つことができ、。

たとえば、Haskellランタイムmainでは、プログラムを構成するすべてのアクションで構成される、呼び出されるアクションを定義する必要があります。次に、ランタイムはこのアクションを実行します。ほとんどの場合、そうすることで純粋なコードを実行します。ランタイムは計算されたデータを使用してI / Oを実行し、データを純粋なコードにフィードバックします。

これは不正行為のように聞こえると文句を言うかもしれません。つまり、アクションを定義し、ランタイムがそれらを実行することを期待することで、プログラマーは通常のプログラムで実行できるすべてのことを実行できます。しかし、Haskellの強力な型システムは、プログラムの純粋な部分と「純粋でない」部分の間に強力な障壁を作成します。たとえば、現在のCPU時間に2秒を単純に追加して印刷することはできません。現在の結果となるアクションを定義する必要があります。 CPU時間。2秒を追加して結果を出力する別のアクションに結果を渡します。ただし、プログラムが多すぎるとスタイルが悪いと見なされます。これは、値が何であるかを知ることができるすべての情報を提供するHaskellの型と比較すると、どのような影響が生じるかを推測することが難しいためです。

例:clock_t c = time(NULL); printf("%d\n", c + 2);Cの場合main = getCPUTime >>= \c -> print (c + 2*1000*1000*1000*1000)とHaskellの場合。演算子>>=を使用してアクションを作成し、最初の結果を関数に渡して2番目のアクションを生成します。これはかなり難解であり、Haskellコンパイラは構文シュガーをサポートしているため、次のように後者のコードを記述できます。

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 ()

後者はかなり必須に見えますね。


1

はいの場合、どのようにして存在できますか?関数型プログラミングの原則に違反していませんか?特に参照の透明性に違反している

純粋に機能的な意味では存在しません。

または、いいえの場合、関数型プログラミングの現在の時刻をどのように知ることができますか?

コンピュータで時刻がどのように取得されるかを知ることは最初に役立つかもしれません。基本的に、時間を追跡するオンボード回路があります(これは、コンピューターが通常小型セルバッテリーを必要とする理由です)。次に、特定のメモリレジスタに時間の値を設定する内部プロセスが存在する可能性があります。基本的には、CPUが取得できる値に要約されます。


Haskellには、何らかのIOプロセスを実行するために作成できるタイプを表す「IOアクション」の概念があります。したがって、time値を参照する代わりに、値を参照しIO Timeます。これらはすべて純粋に機能します。参照しているのではなくtime「タイムレジスタの値を読み取る」という行に沿ったものです。

実際にHaskellプログラムを実行すると、実際にはIOアクションが発生します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.