Tho*_*sen 8 syntax monads scheme interpreter haskell
为了刷新我20年的Haskell经验,我正在浏览https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Adding_Variables_and_Assignment,并在一点上引入以下行以适用op于所有参数.这是为了实现例如(+ 1 2 3 4)
numericBinop op params = mapM unpackNum params >>= return . Number . foldl1 op
Run Code Online (Sandbox Code Playgroud)
我不懂语法,文中的解释有点模糊.
我理解foldl1点函数的作用和方法(unpackNum是辅助函数),但使用Monads和>>=运算符让我有点困惑.
怎么读?
实质上,
mapM unpackNum params >>= return . Number . foldl1 op
Run Code Online (Sandbox Code Playgroud)
由两部分组成.
mapM unpackNum params表示:获取参数列表params.在每个项目上,应用unpackNum:这将产生一个Integer包裹在ThrowsErrormonad中.所以,它并不是一个简单的问题Integer,因为它有机会出错.无论如何,unpackNum在每个项目上使用成功生成全部Integers,或者抛出一些错误.在第一种情况下,我们生成一个新的类型列表[Integer],在第二种情况下,我们(不出所料)抛出错误.因此,此部分的结果类型是ThrowsError [Integer].
第二部分是... >>= return . Number . foldl1 op.这>>=意味着:如果第一部分抛出一些错误,整个表达式也会抛出该错误.如果部件成功生成[Integer]然后继续foldl1 op,将结果包装为a Number,最后用于return将此值作为成功计算注入.
总的来说有monadic计算,但你不应该考虑太多.这里的monadic东西只传播错误,或者如果计算成功则存储普通值.有了一点经验,人们可以专注于成功的价值观,并mapM,>>=,return处理错误案例.
顺便说一句,请注意虽然这本书使用的代码类似action >>= return . f,但这可能是一种糟糕的风格.可以使用相同的效果,fmap f action或者f <$> action,这是表达相同计算的更直接的方式.例如
Number . foldl1 op <$> mapM unpackNum params
Run Code Online (Sandbox Code Playgroud)
这非常接近于忽略错误情况的非monadic代码
-- this would work if there was no monad around, and errors did not have to be handled
Number . foldl1 op $ map unpackNum params
Run Code Online (Sandbox Code Playgroud)
你的问题是关于语法的,所以我将谈谈如何解析该表达式.Haskell的语法非常简单.通俗地说:
>>=,或.)的标识符是中缀(即它们的第一个参数位于标识符的左侧)infix...声明定义)所以只知道这一点,如果我看到:
mapM unpackNum params >>= return . Number . foldl1 op
Run Code Online (Sandbox Code Playgroud)
首先,我知道它必须像解析一样
(mapM unpackNum params) >>= return . Number . (foldl1 op)
Run Code Online (Sandbox Code Playgroud)
为了更进一步,我们需要检查我们在此表达式中看到的两个运算符的固定性/优先级:
Prelude> :info (.)
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in ‘GHC.Base’
infixr 9 .
Prelude> :info (>>=)
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
...
-- Defined in ‘GHC.Base’
infixl 1 >>=
Run Code Online (Sandbox Code Playgroud)
(.)具有更高的优先级(9vs 1for >>=),因此它的参数将更紧密地绑定(即我们首先将它们括起来).但我们怎么知道哪些是正确的呢?
(mapM unpackNum params) >>= ((return . Number) . (foldl1 op))
(mapM unpackNum params) >>= (return . (Number . (foldl1 op)))
Run Code Online (Sandbox Code Playgroud)
...?因为(.)声明infixr它与右侧相关联,这意味着上面的第二个解析是正确的.
Will Ness在评论中指出,(.)是关联的(例如加法),所以这两者恰好在语义上是等价的.
通过对库(或Prelude本例)的一点经验,您可以学习如何正确地解析运算符,而无需考虑太多.
如果做这个练习后,你想了解什么功能呢或它是如何工作的,那么你可以通过点击你感兴趣的功能源和更换右手侧左侧面的出现(即内嵌功能和术语的主体).显然,你可以在头脑或编辑器中做到这一点.