这个多参数类型类可以简化吗?

时间:2015-02-26 08:12:58

标签: haskell typeclass contravariance

我创建了这个类型类来创建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参数上,我实际上希望在tf中完全具有多态性;它们只是类参数,因为实例需要根据它们实例化z

最初我用类型家庭试过这个;该类只有一个参数rt的角色由关联类型占用。我遇到的问题(以及许多其他变体)是即使我可以让它接受类和实例,将它应用于多个参数也会因模糊类型变量而失败。我相信这是因为我没有和haskell沟通,所以我一直在使用相同的r

是否可以在保持f使用的同时恢复dApply的更简单类型?

(我的最终目标是使用它来创建一个包含 {/ em> unFunc |$| fa |*| fb |*| fcApplicative的类型类;给定一对函数Divisible和{ {1}}应该可以使函数func :: a -> b -> c -> z只知道unFunc :: z -> (a, (b, (c, ())))是协变的还是逆变的,而不知道哪个。因此我想要反映f a -> f b -> f c-> f z用法的结构)

1 个答案:

答案 0 :(得分:3)

我认为这里的问题是“Applicative样式”与Divisible的构成方式非常匹配 你坚持要完全采用unFunc |$| fa |*| fb |*| fc格式。毕竟,最直接的写作方式是

unFunc `contramap` (fa `divided` (fb `divided` (fc `divided` conquered)))

Applicative样式完全相反的方式关联。

无论如何强制它进入那种格式会导致代码找到类似Text.Printf的可变长度参数列表重载技巧的内容 - 这是一个肯定的迹象,表明你正在与类型作斗争。

相反,我建议稍微更改一下使用情况,以便它适合ApplicativeDivisible的更自然类型。也许是这样的事情:

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相同的方法。