向一些对单子一无所知的人解释单子类型类的最佳单子类型是什么?我应该使用标准 Haskell 库中的东西还是应该编造一些新类型?
4 回答
我认为让单子模式从实际使用中产生是很重要的。选择几种类型并引导某人解决由单子模式自然表达的问题可能会很有启发性。
在我的脑海中,很容易争论 的好处Maybe
,让某人关注显然会导致的嵌套错误处理,然后谈谈如何
case f x of
Nothing -> Nothing
Just y -> case g y of
Nothing -> Nothing
Just z -> case h z of
Nothing -> Nothing
Just q -> case r q of
Nothing -> Nothing
Just end -> end
实际上是 Haskell 让你抽象出来的一个非常非常常见的模式。
然后讨论配置以及将Config
数据类型传递给许多函数以供它们操作的有用性。很容易写出像这样的代码
go config in =
let (x, y) = f config $ g config $ h config in
in finally config x (we'reDone config y)
但这又是 Haskell 中一个非常常见的模式,虽然很烦人,但有一个减少冗长的通用策略。
最后,将状态突变作为链接内同态来讨论,例如
let state4 = (modify4 . modify3 . modify2 . modify1 :: State -> State) state0
以及这也很烦人,同时还提前修复了您的“修改链”,而不允许您从中间步骤中获取任何信息(至少也没有将其与您的状态一起线程化)。
同样,这可以在 Haskell 中通过一个具有奇怪名称的通用抽象模式非常统一地解决。你听说过关于 Monads 的故事,对吧?
monad(在我看来)是最Maybe
容易理解的。一旦你通过了(简单)代数类型的概念,理解Maybe
monad 是如何工作的就相当简单了。
如果有人在理解 的构造函数时遇到困难Maybe
,您可以为他们编写一个执行基本相同操作的类:
class Maybe(object):
def __init__(self, value=None):
self.__just = value
def just(self):
if self.isJust():
return self.__just
else:
raise ValueError('None')
def isJust(self):
return self.__just is not None
def fmap(self, f):
if self.isJust():
return Maybe(f(self.just()))
else:
return Maybe()
def bind(self, fM):
"""fM must return a value of type Maybe"""
if self.isJust():
return fM(self.just())
else:
return Maybe()
def __repr__(self):
if self.isJust():
return 'Just ({})'.format(self.just())
else:
return 'Nothing'
def head(some_list):
if len(some_list) == 0:
return Maybe()
else:
return Maybe(some_list[0])
def idx(some_list, i):
if idx < len(some_list):
return Maybe(some_list[i])
else:
return Maybe()
print head([1, 2, 3]).bind(
lambda x: Maybe(2 * x)).bind(
lambda x: Maybe(x + 1)).bind(
lambda x: Maybe(x + 3))
print head([[1, 2, 3]]).bind(
lambda xs: idx(xs, 0)).bind(
head).bind(
lambda x: 2 * x)
print head([[1, 2, 3]]).bind(
lambda xs: idx(xs, 1)).bind(
head).bind(
lambda x: 2 * x)
此代码将打印出来
Just (6)
Nothing
Nothing
这与 Haskell 中的 monad 具有相同的功能(或多或少)Maybe
,只是使用类在 Python 中重新实现。Haskell 中的return
函数被构造函数替换,>>=
被替换为.bind
.
我的一个更深刻的见解是,monad 可以被视为命令式编程语言,你可以编写它。所以也许你应该和他们一起建立一种“语言”,这样他们才能掌握抽象的强大程度。
我认为为他们创建一种编程语言将是一个很好的视角。
例如,首先你添加状态
import Data.Map as M
import Control.Monad
import Control.Monad.State
data Variable = IntV Int | StringV String ..
type Context = M.Map String Variable
type Program a = State Context a
然后添加日志记录:
type Program a = WriterT [String] (State Context) a log x = tell [x]
然后添加例外:
类型 Program a = WriterT [String] (StateT Context Either) a
然后你添加延续等。
这样你就可以向他们展示你可以使用 monad 来构建一个对你的问题有好处的环境。在此之后,如果他们感兴趣,您可以向他们展示 monad 的解剖结构以及它们是如何构建的。
例如,首先你展示了 Maybe monad。首先给他们它的纯 lambda 版本:
data Perhaps a = Sure a | Nope
next (Sure a) f = f a
next (Nope) f = Nope
向他们展示如何使用 lambda 链接计算:
small x | x < 100 = Sure x
| otherwise = Nope
between x y z | x < z < y = Sure z
| otherwise = Nope
small 10 `next` (\b -> between 5 20 b)
然后向他们展示如何将其转换为 do 表示法:
small x `next` (\b -> between 5 20 b
`next` (\c -> between 10 14 c))
建议如果我们可以这样写它会很方便:
small x -> \b ->
between 5 20 b -> \c ->
between 10 14 c
然后引入do符号:
b <- small x
c <- between 5 20 b
between 10 14 c
现在他们已经和你一起发明了 do 表示法,你可以从那里解释一些其他的单子。