对于Haskell来说,通过IO类型来建模外部世界。当然,要想彻底理解这个类型,还需阅读后面的内容,这里只是可以简单提一下。IO类型是一个函子,而我们可以用fmap来操作包裹在IO中的值:

module Test where
main :: IO ()
main = do
l <- fmap length getLine
print l 

把上面的程序写到Test.hs文件中,然后使用runhaskell Test.hs运行:

$ runhaskell Test.hs
dwqdad
6

程序会计算你输入的字符串的长度,然后打印出来。这里先不要管do、<-这些语法,后面关于单子的章节会详细讲解。对于fmap length getLine这段代码,你可能觉得有些奇怪,length不应该作用在字符串上吗?实际上,getLine的类型是包裹在IO中的:

getLine :: IO String

这意味着getLine的值需要和外界交互(这里指的是等待用户输入了),而由于IO类型是个函子,所以如果你希望把length作用在IO String类型的值上,就需要使用fmap把函数从String -> Int类型的length,变成IO String -> IO Int类型的fmap length。

从一个更大的视角来看,fmap可以把一个a -> b类型的函数变成f a -> f b类型的函数,从而让函数可以作用在任意类型的包裹上,我们把这个过程叫作升格(lift),即让函数的作用范围从一个范畴进入另一个范畴。在Haskell中,不同的类型类有着不同的升格操作。而对于函子类型类来说,升格操作就是fmap。

从本章起,接下来的若干章将继续关注很多抽象出来的类型类和对应方法的语义。就像学习物理中的动量守恒定律一样,你需要理解Σ m * v = Σ m' * v',但不用记住它,只要记住的是它的物理意义即可。在Haskell中,类型类的方法实现随着实例类型的不同而不同,但是我们并不需要把太多精力放在这些方法的具体实现上,而是要理解抽象出来后的方法,在所有实例上表达的一个共同的计算规则,这就是类型类的方法包含的语义。

评论

本文目前还没有评论……

我要评论

需要登录后才能发言
登录未成功,请修改提交。

× 449
× 1756
× 2435
× 910
× 1
× 1
× 1188
× 0
× 1
× 0
× 2
× 1
× 3
× 3
× 2744
× 817
× 1097