我创建了这个类型类来创建Divisible
个仿函数("逆变量类似于Applicative
" - https://hackage.haskell.org/package/contravariant-1.2/docs/Data-Functor-Contravariant-Divisible.html)可用于多个参数的函数与Applicative
仿函数相同。
我的想法是你写unFunc |$| fa |*| fb |*| fc
,其中:
unFunc :: z -> (a, (b, (c, ())))
fa :: f a
fb :: f b
fc :: f c
为某些f z
仿函数Divisible
获取f
。来自|$|
的{{1}}为divide
,而我的代码中的Data.Functor.Contravariant.Divisible
为|*|
:
dApply
这实际上有效!但是班级{-# LANGUAGE FlexibleContexts
, FlexibleInstances
, FunctionalDependencies
, MultiParamTypeClasses
, TypeFamilies
, UndecidableInstances
#-}
import Control.Applicative ((<$>), (<*>), Applicative)
import Data.Functor.Contravariant.Divisible
( Divisible(divide, conquer)
, divided
)
result :: (b -> c) -> (a -> b) -> (a -> c)
result = fmap
class Divisible f => DivisibleApply f t z r | f t z -> r, r t -> f, r t -> z
where dApply :: (f (a, t) -> f z) -> f a -> r
instance Divisible f => DivisibleApply f () z (f z)
where dApply = (. flip divided conquer)
instance DivisibleApply f b z r' => DivisibleApply f (a, b) z (f (a, b) -> f z)
where dApply = flip (result.result) divided
似乎太复杂了。我实际上只是&#34;切换&#34;在DivisibleApply
参数上,我实际上希望在t
和f
中完全具有多态性;它们只是类参数,因为实例需要根据它们实例化z
。
最初我用类型家庭试过这个;该类只有一个参数r
,t
的角色由关联类型占用。我遇到的问题(以及许多其他变体)是即使我可以让它接受类和实例,将它应用于多个参数也会因模糊类型变量而失败。我相信这是因为我没有和haskell沟通,所以我一直在使用相同的r
。
是否可以在保持f
使用的同时恢复dApply
的更简单类型?
(我的最终目标是使用它来创建一个包含 {/ em> unFunc |$| fa |*| fb |*| fc
和Applicative
的类型类;给定一对函数Divisible
和{ {1}}应该可以使函数func :: a -> b -> c -> z
只知道unFunc :: z -> (a, (b, (c, ())))
是协变的还是逆变的,而不知道哪个。因此我想要反映f a -> f b -> f c-> f z
用法的结构)
答案 0 :(得分:3)
我认为这里的问题是“Applicative
样式”与Divisible
的构成方式非常匹配 你坚持要完全采用unFunc |$| fa |*| fb |*| fc
格式。毕竟,最直接的写作方式是
unFunc `contramap` (fa `divided` (fb `divided` (fc `divided` conquered)))
与Applicative
样式完全相反的方式关联。
无论如何强制它进入那种格式会导致代码找到类似Text.Printf
的可变长度参数列表重载技巧的内容 - 这是一个肯定的迹象,表明你正在与类型作斗争。
相反,我建议稍微更改一下使用情况,以便它适合Applicative
和Divisible
的更自然类型。也许是这样的事情:
func |$| fa |*| fb |*| fc |!| unFunc
显示相似之处的示例代码(我将它留给您实际统一它们。)我还更改了unFunc
的类型以更好地适应此用法。
import Data.Functor.Contravariant
import Data.Functor.Contravariant.Divisible
import Control.Applicative
(|$|) :: Divisible f => t -> f a -> f a
f |$| fa = fa -- For Divisibles, this end does nothing
(|*|) :: Divisible f => f a -> f b -> f (a, b)
fa |*| fb = divided fa fb
(|!|) :: Contravariant f => f b -> (a -> b) -> f a
fz |!| unFunc = contramap unFunc fz
(<!>) :: Applicative f => f z -> t -> f z
fz <!> _ = fz -- For Applicatives, this end does nothing
-- These two functions have the same type except for one using Divisible and the other Applicative
divide3 :: Divisible f => (a -> b -> c -> z) -> (z -> ((a, b), c)) -> f a -> f b -> f c -> f z
divide3 func unFunc fa fb fc = func |$| fa |*| fb |*| fc |!| unFunc
apply3 :: Applicative f => (a -> b -> c -> z) -> (z -> ((a, b), c)) -> f a -> f b -> f c -> f z
apply3 func unFunc fa fb fc = func <$> fa <*> fb <*> fc <!> unFunc
我还可以看到一种替代方法,您可以删除最初的|$|
部分,并使用Applicative
支持组合的事实,其中的对比Divisible
支持应用函数更好,所以您可以使用与Applicative
s {/ 1}}上面使用的Divisible
相同的方法。