我创建此类型类是为了使 Divisible
仿函数(“Applicative
的逆变模拟”- https://hackage.haskell.org/package/contravariant-1.2/docs/Data-Functor-Contravariant-Divisible.html)以几乎相同的方式与多参数函数一起使用作为 Applicative
仿函数。
这个想法是你写unFunc |$|法|*|面子书 |*| fc
,其中:
unFunc :: z -> (a, (b, (c, ())))
fa :: f a
fb :: f b
fc :: f c
得到一个 f z
,对于一些 Divisible
仿函数 f
。 |$|
是 divide
来自 Data.Functor.Contravariant.Divisible
,|*|
是 dApply
来 self 下面的代码:
{-# 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
这确实有效!但是 DivisibleApply
类似乎太复杂了。我实际上只是在 t
参数上“切换”,我实际上想在 f
和 z
中完全多态;它们只是类参数,因为实例需要根据它们实例化 r
。
最初我尝试使用类型族;该类只有一个参数 t
,r
的角色由关联类型承担。我遇到的问题(以及许多其他变体)是,即使我可以让它接受类和实例,将它应用于多个参数也会因类型变量不明确而失败。我相信这是因为我没有与 haskell 沟通我一直在使用相同的 f
。
是否有可能为 dApply
返回更简单的类型,同时仍然保持 unFunc |$|法|*|面子书 |*| fc
用法?
(我的最终目标是使用它来创建一个包含两者 Applicative
和Divisible
的类型类;给定一对函数 func::a -> b -> c -> z
和 unFunc::z -> (a, (b, (c, ())))
应该是可以制作一个函数 f a -> f b -> f c-> f z
只知道 f
是协变或逆变,而不知道是哪个。因此我为什么要镜像Applicative
用法的结构
最佳答案
我认为这里的问题是“Applicative
样式”非常适合 Divisible
的组合方式,因此您没有自然类型如果您坚持完全使用 unFunc |$| ,则给出您的中间结果法|*|面子书 |*| 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
使用与我上面用于 Divisible
相同的方法。
关于haskell - 这个多参数类型类可以简化吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28737332/