我可以将此newtype实现为其他类型的组合吗?

Lyn*_*ynn 12 haskell type-theory category-theory type-kinds polykinds

我编写了一个Const3非常类似的newtype Const,但包含三个给定类型参数中的第一个:

newtype Const3 a b c = Const3 { getConst3 :: a }
Run Code Online (Sandbox Code Playgroud)

我可以为这个新类型定义很多有用的实例,但我必须自己完成所有这些.

但是,我在类型级别上应用的功能类似于该功能

\a b c -> a
Run Code Online (Sandbox Code Playgroud)

@pl告诉我相当于const . const.

双方(.)const具有匹配的NEWTYPE包装:ComposeConst.所以我想我能够写:

type Const3 = Compose Const Const
Run Code Online (Sandbox Code Playgroud)

并自动继承有用的实例,例如:

instance Functor (Const m)
instance (Functor f, Functor g) => Functor (Compose f g)
-- a free Functor instance for Const3!
Run Code Online (Sandbox Code Playgroud)

但GHC不同意:

const3.hs:5:23:
    Expecting one more argument to ‘Const’
    The first argument of ‘Compose’ should have kind ‘* -> *’,
      but ‘Const’ has kind ‘* -> * -> *’
    In the type ‘Compose Const Const’
    In the type declaration for ‘Const3’
Run Code Online (Sandbox Code Playgroud)

这似乎与该种ComposeConst:

*Main> :k Compose
Compose :: (* -> *) -> (* -> *) -> * -> *
*Main> :k Const
Const :: * -> * -> *
Run Code Online (Sandbox Code Playgroud)

所以经过一些搜索,我发现有一个GHC扩展称为PolyKinds允许我做类似的事情:

{-# LANGUAGE PolyKinds #-}
newtype Compose f g a = Compose { getCompose :: f (g a) }
newtype Const a b = Const { getConst :: a }
Run Code Online (Sandbox Code Playgroud)

仿佛通过魔法种类是正确的:

 *Main> :k Compose
 Compose :: (k -> *) -> (k1 -> k) -> k1 -> *
 *Main> :k Const
 Const :: * -> k -> *
Run Code Online (Sandbox Code Playgroud)

但我仍然不能写他们写Const3 = Compose Const Const.

const3.hs:12:23:
    Expecting one more argument to ‘Const’
    The first argument of ‘Compose’ should have kind ‘* -> *’,
      but ‘Const’ has kind ‘* -> k0 -> *’
    In the type ‘Compose Const Const’
    In the type declaration for ‘Const3’
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?有一些聪明的办法做到这一点,这样我就可以收获继承的好处Functor从等情况ConstCompose

(作为旁注,引导我的最初想法Const3是写作:

newtype Const3 a b c = Const3 { getConst3 :: a }

instance Monoid m => Category (Const3 m) where
  id = Const3 mempty
  Const3 x . Const3 y = Const3 (mappend x y)
Run Code Online (Sandbox Code Playgroud)

捕获monoid是单个对象类别的想法.如果有一个解决方案仍然允许我以某种方式编写上述实例,那将是很好的.)

Tik*_*vis 2

令人困惑的\xe2\x80\x94,或者至少让困惑的\xe2\x80\x94 是它的行为*就像一个具体类型,而不是一个类型变量。所以没有PolyKinds,Compose有一个更像是的类型:

\n\n
compose :: (A -> A) -> (A -> A) -> A -> A\n
Run Code Online (Sandbox Code Playgroud)\n\n

至关重要的是,我们不能用Awith替换A -> A,因为它们是不同的类型,因此,按照相同的逻辑,我们不能用*任何一个替换* -> *

\n\n

即使使用PolyKinds,类型仍然不正确。特别是,Compose期望(k -> *)作为它的第一个参数,而你试图给它(k -> (k2 -> *))

\n\n

您被迫返回 kind 的原因*是因为您正在使用newtypes, 而 newtypes 必须返回具体类型(即 kind *)。我试图通过变成Compose一个类型同义词来克服这个问题,它最终具有我们想要的类型(带有PolyKinds):

\n\n
type Compose f g a = (f (g a))\n\n\xce\xbb> :k Compose\nCompose :: (k1 -> k) -> (k2 -> k1) -> k2 -> k\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,使用它仍然给我带来了类似的错误,我不确定我们是否能让它正常工作。出现问题是因为应用于Compose第一个Const给我们提供了一种带有 a 的类型*,可能是因为类型别名的限制,如下所示:

\n\n
\xce\xbb> :k Compose Const\nCompose Const :: (k -> *) -> k -> k1 -> *\n
Run Code Online (Sandbox Code Playgroud)\n

  • 有趣的是,如果你使 `Const` 和 `Compose` 都具有足够的类型多态性,你可以让 GHCi 的 `:kind` 显示正确的类型,例如 `Compose Const Const Int Bool String :: *`,但它仍然没有' t 作为类型注释:“类型同义词 `Const` 应该有 4 个参数,但在表达式中给出了 2 个参数:`42 :: Compose Const Const Int Bool String`” (2认同)