我很难理解这一点.写入符号时,以下两行有何不同?
1. let x = expression
2. x <- expression
Run Code Online (Sandbox Code Playgroud)
我看不出来.有时一个工作,有时一个工作.但两者都很少."了解你是一个哈克尔"说,<-
右侧将符号绑定在左侧的符号上.但这与简单定义x
有let
什么不同?
Die*_*Epp 25
该<-
语句将从monad中提取值,而let
语句则不会.
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)
Run Code Online (Sandbox Code Playgroud)
运行时,程序将询问x的值,因为monadic操作readInt "x"
由<-
语句执行.它不会询问y的值,因为readInt "y"
会对其进行求值,但不会执行生成的monadic操作.
Enter value for x: 123 x :: Int y :: IO Int
既然如此x :: Int
,你可以Int
用它做正常的事情.
putStrLn $ "x = " ++ show x
putStrLn $ "x * 2 = " ++ show (x * 2)
Run Code Online (Sandbox Code Playgroud)
既然如此y :: IO Int
,你不能假装它是常规的Int
.
putStrLn $ "y = " ++ show y -- ERROR
putStrLn $ "y * 2 = " ++ show (y * 2) -- ERROR
Run Code Online (Sandbox Code Playgroud)
bit*_*ket 13
在let
绑定中,表达式可以具有任何类型,您所做的只是为其命名(或在其内部结构上匹配模式).
在<-
版本中,表达式必须具有类型m a
,其中m
是do
块所在的IO
monad.例如,在monad中,此表单的绑定必须IO a
在右侧具有某种类型的值.该a
(所述一元值内)的部分是什么被绑定到在左侧的图案.这使您可以在do
块的有限范围内提取monad的"内容" .
该do
符号,正如你可能已经读过,只是语法糖在一元绑定运营商(>>=
和>>
).x <- expression
去糖expression >>= \x ->
和去糖(和expression
它本身,没有<-
)去糖expression >>
.这只是为定义monadic计算的长链提供了一种更方便的语法,否则它往往会构建一个相当令人印象深刻的嵌套lambda.
let
绑定根本不会脱糖,真的.唯一的区别let
在do
块和let
外部的do
块是该do
版本不需要in
关键字遵循它; 它绑定的名称隐含在do
块的其余部分的范围内.
在let
形式中,expression
a是非monadic值,而a的右侧<-
是monadic表达式.例如,您只能IO t
在第二种绑定中进行I/O操作(类型).详细地说,这两种形式可以大致翻译为(其中==>
显示翻译):
do {let x = expression; rest} ==> let x = expression in do {rest}
Run Code Online (Sandbox Code Playgroud)
和
do {x <- operation; rest} ==> operation >>= (\ x -> do {rest})
Run Code Online (Sandbox Code Playgroud)