我正在努力学习Haskell,在玩弄应用函子时,我发现有一件事困扰着我。
让我们定义以下函数 g ,它返回一些函子:
*Main> let g = pure (2*)
*Main> :t g
g :: (Num a, Applicative f) => f (a -> a)
由于返回类型是某些仿函数,我可以在这两个函数中使用 g 作为参数:
f1 :: (Num a) => [a -> a] -> a
f1 (g:[]) = g 3
f2 :: (Num a) => Maybe (a -> a) -> a
f2 (Just g) = g 4
但这意味着函数 g 返回的值还取决于将要评估的上下文! (它可能是List和Maybe。)这也是懒惰的属性吗?因为到目前为止,我一直在考虑懒惰,以便在需要时计算,但是已经确定它何时被定义( g 在中让表达)。
答案 0 :(得分:7)
正如@augustss所说,它与懒惰无关,而是你正在使用类型类的事实。为了更清楚,您可以通过显式传递包含类定义的所有函数的记录来为类型类建模。这种技术称为字典传递,以防您想要查找更多相关信息。
我们从一些扩展开始。
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
然后给记录类型打包Applicative
应该具有的功能(实际上你还有一个字段说f
是Functor
但是我为简洁起见,请在此省略。
data Applicative f =
Applicative { pure :: forall a. a -> f a
, app :: forall a b. f (a -> b) -> f a -> f b
}
我们可以将您的函数g
定义为记录f
是Applicative
并传达您所描述的行为(我将Num
保留为类但是,类似地,它可以被转换为记录传递。
g :: Num a => Applicative f -> f (a -> a)
g Applicative{..} = pure (2*)
您的两个函数f1
和f2
仍然是有效的定义:
f1 :: Num a => [a -> a] -> a
f1 (g:[]) = g 3
f2 :: Num a => Maybe (a -> a) -> a
f2 (Just g) = g 4
现在,我们希望将它们应用于g
,但问题是:g
有一个函数类型,期望传递Applicative f
条记录。好吧,我们可以定义[]
的{{1}}和Maybe
个实例:
Applicative
然后我们必须选择正确的应用程序来进行类型检查(applicativeList :: Applicative []
applicativeList =
Applicative { pure = (:[])
, app = \ fs as -> fs >>= \ f -> fmap f as
}
applicativeMaybe :: Applicative Maybe
applicativeMaybe =
Applicative { pure = Just
, app = \ fs as -> fs >>= \ f -> fmap f as
}
[]
和f1
Maybe
):
f2