高阶多态是否需要严格的参数顺序?

时间:2016-02-10 08:35:52

标签: haskell syntax higher-kinded-types

阅读LYAH,我偶然发现了这段代码:

Writer w

在尝试理解第一行中的Maybe时,我发现这不是一个完整类型,而是一种带有1个参数的类型构造函数,如Maybe String Writer' }}

看起来很棒,但是如果newtype Writer' a w = Writer' { runWriter :: (a, w) } 的初始类型是使用交换类型参数定义的,如下所示:

instance (Monoid w) => Monad (\* -> Writer' * monoid) where

现在可以实现Monad实例吗?这样的东西,但实际上可以编译的是什么:

\* -> Writer' * monoid

Writer w的想法与if (Regex.IsMatch(toCheck, @"[^&\,\ß]")) { toCheck = toCheck.Replace("&", "and"); toCheck = toCheck.Replace(",", ""); toCheck = toCheck.Replace("ß", "ss"); return toCheck; } 相同: 缺少一个类型参数的类型构造函数 - 这次是第一个。

2 个答案:

答案 0 :(得分:9)

这在Haskell中是不可能的,你需要的是一个类型级的lambda函数,它不存在。

您可以使用类型同义词来定义类型变量的重新排序:

type Writer'' a w = Writer' a w

但您不能为部分应用的类型同义词提供类实例(即使使用TypeSynonymInstances扩展名)。

我写了关于如何将类型级lambda添加到GHC的主题的MSc论文:https://xnyhps.nl/~thijs/share/paper.pdf在类型类实例中使用而不会牺牲类型推断。

答案 1 :(得分:5)

您在这里看到的是Haskell的狭隘设计选择。从概念上讲,如果你“遗漏”它的第一个参数,那么你的Writer'类型就是一个仿函数就足够了。并且可以发明编程语言语法以允许这样的声明。

Haskell社区还没有这样做,因为他们拥有的东西相对简单并且运行良好。这并不是说替代设计是不可能的,但是要采用这样的设计必须:

  1. 在实践中使用不比我们已经拥有的更复杂;
  2. 提供值得转换的功能或优势。
  3. 这概括了Haskell社区使用类型的许多其他方式;通常,将某事物表示为类型区别的选择与语言设计的某些工件相关联。许多monad变换器都是很好的例子,比如MaybeT

    newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
    
    instance Functor m => Functor (MaybeT m) where ...
    instance Applicative m => Applicative (MaybeT m) where ...
    instance Monad m => Monad (MaybeT m) where ...
    instance MonadTrans MaybeT where ...
    

    由于它是newtype,这意味着MaybeT IO String 同构IO (Maybe String);您可以将这两种类型视为同一组值的两个“视角”:

    1. IO (Maybe String)IO操作,可生成Maybe String类型的值;
    2. MaybeT IO StringMaybeT IO操作,可生成String类型的值。
    3. 观点之间的区别在于它们意味着Monad操作的不同实现。在Haskell中,这也与以下狭隘的技术事实有关:

      • 一个String是最后一个类型参数(“值”),另一个Maybe String是;
      • IOMaybeT IOMonad类的不同实例。

      但也许有一种语言设计,你可以说类型IO (Maybe a)可以有一个特定于它的monad,并且与更一般的IO a类型的monad不同。该语言会产生某些复杂性以使这种区别保持一致(例如,确定Monad默认情况下IO (Maybe String)实例的规则以及允许程序员覆盖默认值的规则选择)。我谦虚地打赌,最终结果将不会比我们拥有的更复杂。 TL; DR: Meh。