【问题标题】:Can this multi-parameter type class be simplified?这个多参数类型类可以简化吗?
【发布时间】:2015-02-26 08:12:58
【问题描述】:

我创建了这个类型类以使 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|$| 是来自Data.Functor.Contravariant.Divisibledivide|*| 是来自我下面的代码的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

这确实有效!但是DivisibleApply 类似乎太复杂了。我实际上只是在t 参数上“切换”,我实际上希望在fz 中完全多态;它们只是类参数,因为实例需要根据它们实例化 r

最初我尝试使用类型族;该类只有一个参数t,而r 的角色由关联类型承担。我遇到的问题(以及许多其他变体)是,即使我可以让它接受类和实例,将它应用于多个参数时也会因类型变量不明确而失败。我相信这是因为我没有与haskell 沟通我一直使用相同的f

是否有可能在保持unFunc |$| fa |*| fb |*| fc 用法的同时恢复dApply 的更简单类型?

(我的最终目标是使用它来创建一个包含 both ApplicativeDivisible 的类型类;给定一对函数 func :: a -&gt; b -&gt; c -&gt; zunFunc :: z -&gt; (a, (b, (c, ()))) 应该是可能的制作一个函数f a -&gt; f b -&gt; f c-&gt; f z 只知道f 是协变的或逆变的,却不知道是哪个。因此我为什么要镜像Applicative 用法的结构)

【问题讨论】:

  • 您最终是否使用关联类型lpaste.net/6307517597139075072 尝试了这种变体?
  • @aavogt 很确定这与我的第一次尝试相同,是的。问题是dApply 的中间应用程序产生了模棱两可的类型变量错误,即使完全指定了最终结果的类型
  • unFunc |$| fa |*| fb |*| fc 是否足以在您的情况下触发它,或者您是否正在使用其他东西进行测试? fwiw,通过我的粘贴,ghci 可以推断出unFunc |$| fa |*| fb |*| fc :: Divisible f =&gt; f a
  • @aavogt 嗯,等我准备好我的开发机器后,我会试一试。也许有一些我没有发现的不同之处。
  • @aavogt 这可以完美地回答这个问题。我不知道我是怎么错过的;我想可能当我尝试这么简单的事情时,我仍然在尝试直接实现我的组合 Applicative/Divisible 类,并且在那种情况下得到了我正在谈论的那种模棱两可的类型变量错误。如果你想回答这个问题,我会接受。

标签: haskell typeclass contravariance


【解决方案1】:

我认为这里的问题是“Applicative 样式”与Divisibles 的组合方式非常匹配,所以如果你坚持使用,你就没有自然类型来提供中间结果完全是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

我还可以看到另一种方法,您可以删除初始 |$| 部分并使用 Applicatives 支持组合比 Divisibles 更好地支持函数应用这一事实,因此您可以使用相同的方法对于Applicatives,就像我在上面用于Divisibles 一样。

【讨论】:

  • 我觉得func |$| fa |*| fb |*| fc |!| unFunc 有点搞笑;出于这个原因,我可能会选择它。不过,它确实使“咖喱”的使用变得不那么愉快。是的,我注意到可分割的“喜欢”是正确的联想; |$| = contramap; |*| = divided 我相信与我想要的源文本完全一致 如果 他们是正确的关联。 |$| = divide; |*| = (.: divided) 几乎 可以工作,但您要么需要额外的|*| conquer,要么必须将最后一个|*| 更改为$(取决于最后是否有额外的)。
猜你喜欢
  • 2022-08-08
  • 2018-06-28
  • 2011-06-01
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 2017-09-10
  • 2014-06-21
  • 1970-01-01
相关资源
最近更新 更多