首页 > 解决方案 > 基于两个不同函子的应用类型类

问题描述

有没有类似于Applicative类型类的东西,但是应用程序的每一端都有两个不同的函子?

IE(<*>) :: (Functor f, Functor g) => f (a -> b) -> g a -> f b

标签: haskellfunctorapplicative

解决方案


(根据@dfeuer 在评论中的建议。)

有一种称为日卷积的结构,可让您在执行应用运算时保留两个函子之间的区别,并延迟将一个函子转换为另一个函子的时刻。

Day类型只是一对函数值,以及一个组合它们各自结果的函数:

data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)

请注意,函子的实际返回值是存在的;组合的返回值是函数的返回值。

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

Nat2来自fin包,它用于参数化Vec来自vec的固定大小。)

我们可以很好地将它们压缩在一起:

res :: Day ZipList (Vec Nat2) (Char,Char)
res = (,) <$> day1 <*> day2

然后将 转换Vec为 aZipList并折叠Day

res' :: ZipList (Char,Char)
res' = dap $ trans2 (ZipList . toList) res

ghci> res'
ZipList {getZipList = [('b','f'),('a','o')]}

使用daptrans2函数。

可能的性能问题:当我们将一个函子提升到Day时,另一个被赋予一个虚拟pure ()值。Day但是当将s 与结合起来时,这是自重的(<*>)。通过将函子包装在Lift转换器中,可以更智能地工作,从而为简单的“纯”案例获得更快的操作。


推荐阅读