2016-08-13 9 views
1

私は次のような苦境に苦しんでいます。IO以外の環境でIOアクションを実行する

evalAExpr :: AExpr -> Env -> Int 
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x) 
    where 
    actAccordingly (Left int) = int 
    actAccordingly (Right stmt) = eval stmt env 

eval :: Stmt -> Env -> IO Env` 

getValueOfBinding :: Env -> String -> Either Integer Stmt 

だから何が起こる必要があることはどちらのモナドから得のstmt xと、それは新しい環境に(理由は他のプロセスと、IOで発生する必要がある)xを評価すべきであるということである、後の私は環境から正しい値を返すことができます。しかし、ハスケルの私の限られた知識から、私はIOから出ることはできません。

例:は、すべてevalAExprが機能しているので(現在1つ以上あります)、Env atmを返すため、現在のコードでは動作しません。これを変更しなければならない場合は、評価do文とreturn文をすべて追加することになります。 > 50〜60回の発生。確かに。そうすれば、コードを見るのが面倒になるでしょう。私がIOを必要とするのは、この評価が(ある時点で)mBot(http://makeblock.com/mbot-stem-educational-robot-kit-for-kids/)からデータを収集するだけでなく、キーボードからデータを収集する必要があるからです(getLine経由で完了)。

私はそれを整然とし、整理しておくことができるようにこのコードを再加工する方法?

もっとコードが必要な場合は、気軽にping/mentionをしてください。私は要求されたコード行を提供します。要求されたよう

もっとコード:EvalAExprが使用されている方法を示し

  • コード。に - 唯一の重要な変更が本当にあり

    evalAExprIO :: AExpr -> Env -> IO Int 
    evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x) 
        where 
        actAccordingly (Left int) = return int 
        actAccordingly (Right stmt) = eval stmt env 
    

    :あなたは私たちは、新しいバージョンevalAExprIOと呼ぶことにしますevalAExpr

    のモナドバージョンを記述する必要が

-- Relational operators evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env evalBExpr (RBinary Less a b) env = evalAExpr a env < evalAExpr b env evalBExpr (RBinary Equal a b) env = evalAExpr a env == evalAExpr b env

+1

関数がIOを実行する場合は、IOタイプを返す必要があります。それ以外の場合、関数は副作用がないと見なしています。もしこれが嘘であれば、コンパイラはそれを見つけます。これを回避するには合理的な方法はありませんが、IOを実行する機能のタイプにIOを追加することはできません。 – chi

+1

おそらく、評価ルーチンでIOを使用する必要がある理由を説明する必要があります。選択肢があるかもしれません... – ErikR

+0

あなたの言語の例を挙げてください。通常のPython/Javascriptのような命令的な言語ですか? – ErikR

答えて

1

Leftケース 機能actAccordinglyは012の代わりにreturn intを使用します。

evalAExprIOのタイプシグネチャが変更されましたが、これはGHCが推測できるので、変更する方法を知っておく必要はありません。 です。

更新

変更:

evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env 

へ:

evalBexpr (RBinary Greater a b) env = liftM2 (>) (evalAExpr a env) (evalExpr b env) 

(。liftM2の輸入Control.Monad)

+0

しかし、私はそのIO intを元のintを返す関数に戻しますか? – MrKickkiller

+0

'evalAExpr'を呼び出すコードをいくつか表示してください。変更する必要があることがわかります。 – ErikR

+0

元の記事を参照してください。下部にEvalAExprを使用するコードを追加しました。私はちょうど私がちょうどタイプを一致させるためにどこでも返すことを書くことになっていると確信していますか? – MrKickkiller

2

ハスケルの私の限られた知識から私はca IOから外に出ることはありません

修正済み!正当な理由があります:現在のシグネチャでは、関数を使用する人は、実際には純粋な、参照可能な透明な計算であることを知っているので、それに応じて不注意(怠惰など)になる可能性があります。 IOをシグネチャで明示的に指定しなくてもかまいませんでしたら、すべてのコードを簡単に破ることができます。

もちろん、IOを行う必要があることに気づいただけであれば、あなたは本当に少しジレンマになります。今すぐすべての署名を変更するのは良い方法ではありません。

しかし、あなたが最初の場所で型シノニム‡を使用することによって、これを防ぐことができた:あなたは、あなたはできる、すべての後にIOが必要であることに気付いすると、その後、もともと

type Evaluation a = Identity a 

-- ... lots of functions with an `Evaluation` result ... 

evalAExpr :: AExpr -> Env -> Evaluation Int 
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x) 
    where 
    actAccordingly (Left int) = return int 
    actAccordingly (Right stmt) = someThingThatDidn'tYetRequireIO 

を持っていただろう場合ちょうど大きなプロジェクトでは

type Evaluation a = IO a 

{- same as before: 
evalAExpr :: AExpr -> Env -> Evaluation Int 
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x) 
    where 
    actAccordingly (Left int) = return int -} 
    actAccordingly (Right stmt) = eval stmt env 

にそれを変更した、Evaluationモナドは、通常、あなただけの下にIOのための余分なレイヤを追加したいモナド変圧器スタックにするだろう。


誰がfunction that shall not be namedについての私に思い出させた場合、私は頭with a nuclear missileについてのそれらを打つものとします。

dfeuer発言として、newtypeが良いかもしれません。

+0

これは実際の任務であり、私自身のプロジェクトではないという意味で、私はあなたの核ミサイルへのあなたの少しの介入も考慮しません。P – MrKickkiller

+0

私はこの種のほとんどに同意しますが、類義語の助言は徹底的に同意します。柔軟性を維持したり、抽象化を強制するためにタイプ同義語を使用しようとする試みから、良いものは見たことがありません。それがnewtypesとtypeクラスのためのものです。型同義語は、「レンズ」でよく見られるように、複雑ではあるが具体的な型の略語として最も有用である。 – dfeuer

+1

@dfeuerそれから私は常にそれらを使用し、私が大きく恩恵を受けたように感じるので、非常に異なる開発スタイルを持たなければなりません。その中には次のような利点があります。 'OtherPlayerID'、' Store'や 'Name'(' String'や 'Text'?)のような"本当の "型のプレースホルダです。型に多くの変数がある場合は読みやすさが向上しました。 'Result a'と' ResultM a'と 'ResultIO a'との間の精神的なつながりをより速く... – MarLinn