を行うことでバインディング。 do表記で書くとき、次の2つの行はどう違うのですか?「< - 」私は苦労これを把握してい記法
1. let x = expression
2. x <- expression
私はそれを見ることができません。時には1つは動作し、もう1回は動作します。しかし、まれに両方。 "Learn you a akkell"は、<-
は、左側のシンボルに右側をバインドすると言います。しかし、単純にx
をlet
と定義することとはどのように違いますか?
を行うことでバインディング。 do表記で書くとき、次の2つの行はどう違うのですか?「< - 」私は苦労これを把握してい記法
1. let x = expression
2. x <- expression
私はそれを見ることができません。時には1つは動作し、もう1回は動作します。しかし、まれに両方。 "Learn you a akkell"は、<-
は、左側のシンボルに右側をバインドすると言います。しかし、単純にx
をlet
と定義することとはどのように違いますか?
<-
文はモナドから値を抽出し、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
の形式では、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})
、式は任意の型を持つことができ、あなたがやっているすべては、名前(またはその内部構造上のパターンマッチング)を与えています。 <-
バージョンで
、式はm
がdo
ブロックが入っているものは何でもモナドであるタイプm a
を、持っている必要があります。IO
モナドでは、例えば、この形式のバインディングが右側にタイプIO a
のいくつかの値を持っている必要がありますので手持ち側。 a
部分(モナドの値の内側)は、左側のパターンにバインドされている部分です。これにより、do
ブロックの範囲内でモナドの「内容」を抽出することができます。
do
という表記は、読んでもわかるように、モナド結合演算子(>>=
と>>
)の構文上の砂糖だけです。 (<-
なしで単独で、)expression >>= \x ->
とexpression
からx <- expression
脱糖expression >>
に脱糖。これは、モナド計算の長い連鎖を定義するためのより便利な構文を提供します。そうでなければ、ネストされたラムダのかなり印象的な集合を構築する傾向があります。
let
バインディングはまったく糖尿病ではありません。 do
ブロックのlet
ブロックとdo
ブロックのlet
ブロックの唯一の違いは、do
バージョンにはin
キーワードが続く必要がないことです。バインドする名前は暗黙的にdo
ブロックの残りのスコープに含まれます。
ハスケルは、タイプ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次の簡単な式を考えてみましょう:
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
に入れるものがありません。
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
の値(ファイルやコマンドライン、またはランダムジェネレータから取得した値)でも動作します。