假设我有一个Monad data Concurrent a = Concurrent ((a -> Action) -> Action)
和一个类型Action
,它允许各种可能的IO Actions
。
说我想写一个函数action :: Concurrent a -> Action
,有效地编写自定义解包。第一步可能是编写函数action2 :: ((a -> Action) -> Action) -> Action
我有几个问题:
我的理解是a
是一个严格的变量,例如一个类型变量,由编译器强制转换为某种类型。
这使得((a -> Action) -> Action)
成为一个函数(让我们称之为f1),它接受类型(a -> Action)
的函数,并返回一个Action,对于某种任意类型,和((a -> Action) -> Action) -> Action
,一个接受上述函数f1的函数,运行它,并返回结果。
假设以上是正确的,我的问题确实是:
在Monad的背景下,这有什么意义?说我有一个功能:
stringToAction :: (String -> Action)
stringToAction [] = Stop
stringToAction x = Atom $ putStr x
所以:t stringToAction
履行了(a - > Action)的合同
但是:t stringToAction" 7"类型(String -> Action) -> String -> Action
我是否必须编写(a -> Action) -> a -> Action
类型的辅助函数(例如)
higherOrderStringToAction :: (String -> Action) -> String -> Action
higherOrderStringToAction f x = f x
然后在传递的函数上调用它(a - > Action)?
stringToActionHelper = higherOrderStringToAction stringToAction
我是否必须为我希望处理的每种可能类型的a
编写一组不同的辅助函数?
然后,更令人担忧的是,我是否必须处理Monad Instance定义中的所有内容? e.g
Concurrent 7
如何知道如何将7转换为动作?我是否必须在a
定义和分支中询问>>=
的类型?我怎么会这样做?
或者我(这显然可能)完全忽略了这一点和/或直接想到这个错误?
答案 0 :(得分:1)
假设我有一个Monad
data Concurrent a = Concurrent ((a -> Action) -> Action)
和一个类型Action
,它允许各种可能的IO Actions
。
我将假设您无缘无故地将“IO动作”放入反引号中。我不知道你是否对你头脑中发生的事情有一个可靠的模型,但由于Haskell是一种纯语言,IO x
的含义是“一个程序,在运行时产生x
” (换句话说,Haskell对功能I / O的解决方案是参与元编程而不是其他编程。)因此,在实践中Action
听起来像是IO ()
的类型同义词或东西。
Hoogle对(a -> IO x) -> IO x
的搜索显示,这是withMVar m
的输出类型,也与fixIO
类型相关。换句话说:您的Concurrent
就是要包含a
类型的某些值并保留以供日后使用。
如果我们定义data C b a = C ((a -> b) -> b)
,我们可以最明确地看到这一点,以便您的Concurrent
实际上只是特殊情况C Action
。这里的技巧是这个函数类型forall b. (a -> b) -> b
实际上是Identity
monad newtype Identity a = Identity {runIdentity :: a}
的Church编码。反过来我们可以说更普遍的monad是:
instance Monad (C b) where
return x = C ($ x)
(C w_wx) >>= my_x = my_x (w_wx id)
所以你为Concurrent
所写的内容实际上是身份monad的一个特例。
所以
:t stringToAction
履行了(a - > Action)的合同但是:t stringToAction“7”的类型为(String -> Action) -> String -> Action
假。 stringToAction "7"
的类型为Action
,因为它类型String -> Action
的函数应用于String
类型的值。
我是否必须编写
类型的辅助函数(a -> Action) -> a -> Action
您所写的内容有一个名称,它是名为id
的函数,或者更具体地说是其类型特化($)
。此外,上面的这种类型没有您想要推送到data Concurrent a = Concurrent ((a -> Action) -> Action)
构造函数的类型。相反,你想要($ "myString") :: (String -> Action) -> Action
之类的东西将该字符串嵌入你的身份monad。
Concurrent 7如何知道如何将7转换为Action?我是否必须在
a
定义和分支中询问>>=
的类型?我怎么会这样做?
没有。您的Concurrent
monad实际上只是Identity
monad,将7
转换为Action
的过程由您传递的Int -> Action
函数完成参与辩论。 (就此而言,如果你写Concurrent 7
,你会从GHC那里得到一个令人讨厌的错误,说“我不知道类型(a -> Action) -> Action
是Num
类型类的元素,是什么到底是怎么回事?!“