Nea*_*der 3 haskell functor applicative
Is there something similar to the Applicative
type class, but where there are two functors for each side of the application which are different?
i.e. (<*>) :: (Functor f, Functor g) => f (a -> b) -> g a -> f b
(在评论中来自@dfeuer的建议。)
有一种称为“ 日卷积”的构造,可让您在执行应用操作时保留两个函子之间的区别,并延迟将一个函子转换为另一个函子的时间。
该Day
类型仅是一对函数值,以及一个组合其各自结果的函数:
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
Run Code Online (Sandbox Code Playgroud)
注意,函子的实际返回值已存在。组合的返回值是函数的返回值。
Day
与组合应用函子的其他方式相比,具有优势。与相比Sum
,该组合仍然适用。与不同Compose
,合成是“无偏的”,并且不施加嵌套顺序。与不同Product
,它使我们能够轻松地将应用操作与不同的返回类型结合起来,我们只需要提供合适的适配器函数即可。
例如,这是两个Day ZipList (Vec Nat2) Char
值:
{-# LANGUAGE DataKinds #-}
import Data.Functor.Day -- from "kan-extensions"
import Data.Type.Nat -- from "fin"
import Data.Vec.Lazy -- from "vec"
import Control.Applicative
day1 :: Day ZipList (Vec Nat2) Char
day1 = Day (pure ()) ('b' ::: 'a' ::: VNil) (flip const)
day2 :: Day ZipList (Vec Nat2) Char
day2 = Day (ZipList "foo") (pure ()) const
Run Code Online (Sandbox Code Playgroud)
(Nat2
来自fin包,用于Vec
从vec参数化固定大小。)
我们可以将它们压缩在一起:
res :: Day ZipList (Vec Nat2) (Char,Char)
res = (,) <$> day1 <*> day2
Run Code Online (Sandbox Code Playgroud)
然后将转换Vec
为ZipList
并折叠Day
:
res' :: ZipList (Char,Char)
res' = dap $ trans2 (ZipList . toList) res
ghci> res'
ZipList {getZipList = [('b','f'),('a','o')]}
Run Code Online (Sandbox Code Playgroud)
可能的性能问题:当我们将一个函子提升到时Day
,另一个函子将被赋予一个虚拟pure ()
值。但这是将Day
s与组合时的净重(<*>)
。通过将函子包装在Lift
变压器中,可以使工作更智能,从而在简单的“纯”情况下获得更快的运行速度。