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

Ily*_*gin 7 syntax haskell higher-kinded-types

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

newtype Writer w a = Writer { runWriter :: (a, w) }

instance (Monoid w) => Monad (Writer w) where  
  return x = Writer (x, mempty)  
  (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')  
Run Code Online (Sandbox Code Playgroud)

虽然试图了解到底是什么Writer w在第一线,我发现这不是一个完整的类型,而是一种类型构造与1种说法,就像MaybeMaybe String

看起来很棒,但如果Writer'使用交换类型参数定义初始类型if ,如下所示:

newtype Writer' a w = Writer' { runWriter :: (a, w) } 
Run Code Online (Sandbox Code Playgroud)

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

instance (Monoid w) => Monad (\* -> Writer' * monoid) where
Run Code Online (Sandbox Code Playgroud)

这个想法\* -> Writer' * monoidWriter w:一个类型构造函数缺少一个类型参数 - 这次是第一个.

xny*_*hps 9

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

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

type Writer'' a w = Writer' a w
Run Code Online (Sandbox Code Playgroud)

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

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


Lui*_*las 5

你在这里看到的是Haskell的狭隘设计选择.从概念上讲,Writer'如果你"遗漏"它的第一个参数,你的类型就是一个仿函数,这是完全合理的.并且可以发明编程语言语法以允许这样的声明.

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

  1. 在实践中使用不比我们已经拥有的更复杂;
  2. 提供值得转换的功能或优势.

这概括了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 ...
Run Code Online (Sandbox Code Playgroud)

因为它是a newtype,这意味着它MaybeT IO String同构IO (Maybe String); 您可以将这两种类型视为同一组值上的两个"透视图":

  1. IO (Maybe String)是一个IO产生类型值的动作Maybe String;
  2. MaybeT IO String是一个MaybeT IO产生类型值的动作String.

透视图之间的区别在于它们意味着Monad操作的不同实现.在Haskell中,这也与以下狭隘的技术事实有关:

  • 一个String是最后一个类型参数("值"),另一个Maybe String是;
  • IOMaybeT IO为该Monad课程设置不同的实例.

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

  • 具体到Haskell及其社区.一些类别理论符号确实显示了不同的惯例 - 人们谈论诸如"* - ×B*仿函数"之类的东西,其中破折号指的是像*A×B*这样的表达式中的"洞".也许有人可以想出一个语言设计,你可以说`实例Monoid m => Monad(Writer'_ m)`或`实例Monoid m => Monad(\ a - > Writer'am)`或其他东西.我认为,我们在Haskell中没有这个的主要原因是因为我们的工作做得很好! (2认同)