2012-03-29 24 views
21

を行うことでバインディング。 do表記で書くとき、次の2つの行はどう違うのですか?「< - 」私は苦労これを把握してい記法

1. let x = expression 
2. x <- expression 

私はそれを見ることができません。時には1つは動作し、もう1回は動作します。しかし、まれに両方。 "Learn you a akkell"は、<-は、左側のシンボルに右側をバインドすると言います。しかし、単純にxletと定義することとはどのように違いますか?

答えて

25

<-文はモナドから値を抽出し、letステートメントは以下となります。詳細には、2つの形式がおおよそ(==>翻訳を示す場合)として翻訳することができますない。

import Data.Typeable 

readInt :: String -> IO Int 
readInt s = do 
    putStrLn $ "Enter value for " ++ s ++ ": " 
    readLn 

main = do 
    x <- readInt "x" 
    let y = readInt "y" 
    putStrLn $ "x :: " ++ show (typeOf x) 
    putStrLn $ "y :: " ++ show (typeOf y) 

実行すると単項アクションreadInt "x"<-文で実行されるため、プログラムは、xの値を求めます。 readInt "y"が評価されますが、結果として得られるモナドのアクションは実行されないため、yの値は求められません。

 
Enter value for x: 
123 
x :: Int 
y :: IO Int 

x :: Intので、あなたはそれで正常なIntことを行うことができます。

putStrLn $ "x = " ++ show x 
putStrLn $ "x * 2 = " ++ show (x * 2) 

y :: IO Intので、あなたはそれが通常のIntだというふりをすることはできません。結合let

putStrLn $ "y = " ++ show y -- ERROR 
putStrLn $ "y * 2 = " ++ show (y * 2) -- ERROR 
8

の形式では、expressionは非モナド型の値ですが、<-の右側はモナド式です。たとえば、第2種のバインドでは、I/O操作(タイプIO t)のみが可能です。

do {let x = expression; rest} ==> let x = expression in do {rest} 

do {x <- operation; rest} ==> operation >>= (\ x -> do {rest}) 
13

、式は任意の型を持つことができ、あなたがやっているすべては、名前(またはその内部構造上のパターンマッチング)を与えています。 <-バージョンで

、式はmdoブロックが入っているものは何でもモナドであるタイプm aを、持っている必要があります。IOモナドでは、例えば、この形式のバインディングが右側にタイプIO aのいくつかの値を持っている必要がありますので手持ち側。 a部分(モナドの値の内側)は、左側のパターンにバインドされている部分です。これにより、doブロックの範囲内でモナドの「内容」を抽出することができます。

doという表記は、読んでもわかるように、モナド結合演算子(>>=>>)の構文上の砂糖だけです。 (<-なしで単独で、)expression >>= \x ->expressionからx <- expression脱糖expression >>に脱糖。これは、モナド計算の長い連鎖を定義するためのより便利な構文を提供します。そうでなければ、ネストされたラムダのかなり印象的な集合を構築する傾向があります。

letバインディングはまったく糖尿病ではありません。 doブロックのletブロックとdoブロックのletブロックの唯一の違いは、doバージョンにはinキーワードが続く必要がないことです。バインドする名前は暗黙的にdoブロックの残りのスコープに含まれます。

2

ハスケルは、タイプIO aのタイプの命令アクションを表現することによって、純粋な関数型プログラミングによる副作用プログラミングを調整します。これはタイプaの結果を生成する命令アクションのタイプです。

本の結果の一つは、式の値に変数を結合して、アクションを実行した結果にそれを結合することは別物であるということです。

x <- action  -- execute action and bind x to the result; may cause effect 
let x = expression -- bind x to the value of the expression; no side effects 

のでgetLine :: IO Stringは、アクションあり、そのline1 ++ line2 :: String一方

do line <- getLine -- side effect: read from stdin 
    -- ...do stuff with line 

は、純粋な表現であり、そしてletで使用する必要があります:

それは次のように使用しなければならないことを意味
do line1 <- getLine   -- executes an action 
    line2 <- getLine   -- executes an action 
    let joined = line1 ++ line2 -- pure calculation; no action is executed 
    return joined 
2

ここでは、その違いを示す簡単な例を示します。 2次の簡単な式を考えてみましょう:

letExpression = 2 
bindExpression = Just 2 

あなたが取得しようとしている情報は、数2です。ここ は、あなたがそれを行う方法です。

let x = letExpression 
x <- bindExpression 

letは直接xに値2を置きます。 <-は、Justから値2を抽出し、xに格納します。

あなたはこれらの二つの表記は交換できませんなぜ、その例を見ることができます。

let x = bindExpressionは直接xに値Just 2を置きます。 x <- letExpressionは、抽出してxに入れるものがありません。

3

letは、任意の値に名前を割り当てたり、パターンマッチしたりするだけです。

<-のために、私たちが最初に離れて(本当に)謎のIOモナドからのステップが、リストまたはMaybeのような「コンテナ」の概念を持ってモナドを、考えてみましょう。その後、<-は、そのコンテナの要素を「アンパック」するだけではありません。 「戻す」の反対の操作はreturnです。このコードを考えてみましょう:

add m1 m2 = do 
    v1 <- m1 
    v2 <- m2 
    return (v1 + v2) 

それは二つの容器の要素を「アンパック」、一緒に値を追加し、同じモナドで再びそれをラップします。これは、要素のすべての可能な組み合わせを取って、リストで動作します:

main = print $ add [1, 2, 3] [40, 50] 
--[41,51,42,52,43,53] 

実際にあなたにもadd m1 m2 = [v1 + v2 | v1 <- m1, v2 <- m2]を書くことができリストの場合。しかし、私たちのバージョンは、あまりにも、Maybe秒で動作します:

main = print $ add (Just 3) (Just 12) 
--Just 15 
main = print $ add (Just 3) Nothing 
--Nothing 

IOはまったくその違いはありません。これは単一の値のコンテナですが、ウイルスなどの「危険な」危険な値であり、直接触れてはいけません。 do -Blockはここではガラス製の格納容器、<-は内部のものを操作する組み込みの「手袋」です。 returnでは、準備ができたら完全な完全なコンテナ(危険なコンテンツだけでなく)を提供します。ちなみに、add関数はIOの値(ファイルやコマンドライン、またはランダムジェネレータから取得した値)でも動作します。

関連する問題