我试图了解一些代码,并且让自己纠结得很好。请帮助我理解我的逻辑或缺乏逻辑......
开始:
*Main> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
如果我只想让f a
成为一个带有一个参数的函数,那么它就没有问题了:
*Main> :t \f -> fmap f (undefined :: String -> Int)
\f -> fmap f (undefined :: String -> Int) :: (Int -> b) -> String -> b
我可以在第二个参数中传入String
,生成Int
,然后使用第一个参数中的函数生成b
。
现在,我希望f a
是一个带有两个参数的函数,所以我将其替换为:
*Main> :t \f -> fmap f (undefined :: String -> Int -> Bool)
\f -> fmap f (undefined :: String -> Int -> Bool)
:: ((Int -> Bool) -> b) -> String -> b
此时,我很困惑。我已经提供了从String
和Int
转换为Bool
的功能。我现在如何提供另一个 Int -> Bool
转换为b
的功能?这是非感性的还是我没有读到这个权利?
或许这可能是仿函数中的仿函数,需要做更多才能使其有意义?在哪种情况下,是什么?
答案 0 :(得分:3)
在Haskell中实际上没有带有两个参数的函数。每个函数都只有一个参数。
特别是,String -> Int -> Bool
是一个接受一个String
参数的函数。 (当然,知道结果又是一个函数,你可以使用它,好像它是一个带有两个参数的函数。)所以如果你想用f a
统一它,你需要
f ~ (String->)
a ~ Int->Bool
确实Int->Bool
本身可以被解释为仿函数应用†
f ~ (String->)
g ~ (Int->)
b ~ Bool
以便String->Int->Bool ~ f (g b)
;从而
\f -> fmap (fmap f) (undefined :: String -> Int -> Bool)
:: (Bool -> b) -> String -> Int -> b
我认为函子函数族不是一个很好的例子来掌握函子/应用程序/ monad的属性。列表和maybes通常不那么容易混淆;而不是普通的函数仿函数,当你需要这个功能时,首选等价的Reader
(双关语不是)。
关于你的原始表达,这实际上并非毫无意义。如果我们将它翻译成驯兽师,我们可以写一下
> fmap ($2) [(>1), (>2), (>3)]
[True, False, False]
使用函数函数可以完成同样的事情:
> fmap ($2) (<) 1
True
> fmap ($2) (<) 2
False
> fmap ($2) (<) 3
False
当然,这个例子有点太简单了,但你也可以实现非常有用的。
† 请注意,f
和g
实际上不是同一个仿函数。我们倾向于将它们称为“ 函数函子”,但实际上,对于(->)
构造函数的每个部分应用程序,您都会获得不同的函子。这意味着,即使有Monad (a->)
个实例,您也无法以任何方式统一这两个层。