Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 具有文本和内射后继者的类型级NAT?(N元组合)_Haskell_Ghc_Dependent Type - Fatal编程技术网

Haskell 具有文本和内射后继者的类型级NAT?(N元组合)

Haskell 具有文本和内射后继者的类型级NAT?(N元组合),haskell,ghc,dependent-type,Haskell,Ghc,Dependent Type,我将其概括为一个n-ary组合,但我在使界面美观方面遇到了困难。也就是说,我不知道如何在类型级别使用数字文本,同时仍然能够在继承者上进行模式匹配 滚动我自己的NAT 使用roll my own NAT,我可以使n-ary编写工作正常,但我只能将n作为迭代后继项传递,而不能作为文本传递: {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-}

我将其概括为一个
n
-ary组合,但我在使界面美观方面遇到了困难。也就是说,我不知道如何在类型级别使用数字文本,同时仍然能够在继承者上进行模式匹配

滚动我自己的NAT 使用roll my own NAT,我可以使
n
-ary编写工作正常,但我只能将
n
作为迭代后继项传递,而不能作为文本传递:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}

module RollMyOwnNats where

import Data.List (genericIndex)

-- import Data.Proxy
data Proxy (n::Nat) = Proxy

----------------------------------------------------------------
-- Stuff that works.

data Nat = Z | S Nat

class Compose (n::Nat) b b' t t' where
  compose :: Proxy n -> (b -> b') -> t -> t'

instance Compose Z b b' b b' where
  compose _ f x = f x

instance Compose n b b' t t' => Compose (S n) b b' (a -> t) (a -> t') where
  compose _ g f x = compose (Proxy::Proxy n) g (f x)

-- Complement a binary relation.
compBinRel :: (a -> a -> Bool) -> (a -> a -> Bool)
compBinRel = compose (Proxy::Proxy (S (S Z))) not

----------------------------------------------------------------
-- Stuff that does not work.

instance Num Nat where
  fromInteger n = iterate S Z `genericIndex` n
-- I now have 'Nat' literals:
myTwo :: Nat
myTwo = 2
-- But GHC thinks my type-level nat literal is a 'GHC.TypeLits.Nat',
-- even when I say otherwise:
compBinRel' :: (a -> a -> Bool) -> (a -> a -> Bool)
compBinRel' = compose (Proxy::Proxy (2::Nat)) not
{-
    Kind mis-match
    An enclosing kind signature specified kind `Nat',
    but `2' has kind `GHC.TypeLits.Nat'
    In an expression type signature: Proxy (2 :: Nat)
    In the first argument of `compose', namely
      `(Proxy :: Proxy (2 :: Nat))'
    In the expression: compose (Proxy :: Proxy (2 :: Nat)) not
-}
使用
GHC.TypeLits.Nat
使用
GHC.TypeLits.Nat
,我得到了类型级别的Nat文本,但我找不到后续构造函数,使用类型函数
(1+)
也不起作用,因为GHC(7.6.3)无法推断类型函数的内射性:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module UseGHCTypeLitsNats where

import GHC.TypeLits

-- import Data.Proxy
data Proxy (t::Nat) = Proxy

----------------------------------------------------------------
-- Stuff that works.

class Compose (n::Nat) b b' t t' where
  compose :: Proxy n -> (b -> b') -> t -> t'

instance Compose 0 b b' b b' where
  compose _ f x = f x

instance (Compose n b b' t t' , sn ~ (1 + n)) => Compose sn b b' (a -> t) (a -> t') where
  compose _ g f x = compose (Proxy::Proxy n) g (f x)

----------------------------------------------------------------
-- Stuff that does not work.

-- Complement a binary relation.
compBinRel , compBinRel' :: (a -> a -> Bool) -> (a -> a -> Bool)
compBinRel = compose (Proxy::Proxy 2) not
{-
    Couldn't match type `1 + (1 + n)' with `2'
    The type variable `n' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    In the expression: compose (Proxy :: Proxy 2) not
    In an equation for `compBinRel':
        compBinRel = compose (Proxy :: Proxy 2) not
-}
{-
    No instance for (Compose n Bool Bool Bool Bool)
      arising from a use of `compose'
    The type variable `n' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there is a potential instance available:
      instance Compose 0 b b' b b'
-}
compBinRel' = compose (Proxy::Proxy (1+(1+0))) not
{-
    Couldn't match type `1 + (1 + 0)' with `1 + (1 + n)'
    NB: `+' is a type function, and may not be injective
    The type variable `n' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Expected type: Proxy (1 + (1 + 0))
      Actual type: Proxy (1 + (1 + n))
    In the first argument of `compose', namely
      `(Proxy :: Proxy (1 + (1 + 0)))'
-}
我同意这里的代码更优雅、更一般——具体地说,编写
()总是很容易的。(.) . ...
n
次)而不是
compose(Proxy::Proxy n)
——但是我很沮丧,我不能让
n
元组成像我预期的那样工作。此外,对于
GHC.TypeLits.Nat
的其他用法,我似乎也会遇到类似的问题,例如,在尝试定义类型函数时:

type family   T (n::Nat) :: *
type instance T 0     = ...
type instance T (S n) = ...
更新:已接受答案的摘要和改编 在公认的答案中有很多有趣的东西, 但对我来说关键是GHC 7.6中的模板Haskell技巧 解决方案:这样可以有效地将类型级别的文字添加到GHC中 7.6.3版本,已经有内射后继版本

使用上面的类型,我通过TH定义文字:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DataKinds #-}

module RollMyOwnLiterals where

import Language.Haskell.TH

data Nat = Z | S Nat

nat :: Integer -> Q Type
nat 0 = [t| Z |]
nat n = [t| S $(nat (n-1)) |]
我已将我的
Nat
声明移动到新模块中,以避免 导入循环。然后,我修改我的
RollMyOwnNats
模块:

+import RollMyOwnLiterals
...
-data Nat = Z | S Nat
...
+compBinRel'' :: (a -> a -> Bool) -> (a -> a -> Bool)
+compBinRel'' = compose (Proxy::Proxy $(nat 2)) not

编辑:重写答案。它变得有点笨重(还有一辆小车)

GHC 7.6 由于类型级别
Nat
s在某种程度上。。。不完整(?)在GHC 7.6中,实现所需功能的最简单方法是GADT和类型族的组合

{-# LANGUAGE GADTs, TypeFamilies #-}

module Nats where

-- Type level nats
data Zero
data Succ n

-- Value level nats
data N n f g where
    Z :: N Zero (a -> b) a
    S :: N n f g -> N (Succ n) f (a -> g)

type family Compose n f g
type instance Compose Zero (a -> b) a = b
type instance Compose (Succ n) f (a -> g) = a -> Compose n f g

compose :: N n f g -> f -> g -> Compose n f g
compose Z f x = f x
compose (S n) f g = compose n f . g
这个特定实现的优点是它不使用类型类,因此
compose
的应用程序不受单态限制。例如,
compBinRel=compose(S(sz))not
将在没有类型注释的情况下进行类型检查

我们可以用一个小小的模板Haskell使它变得更好:

{-# LANGUAGE TemplateHaskell #-}

module Nats.TH where

import Language.Haskell.TH

nat :: Integer -> Q Exp
nat 0 = conE 'Z
nat n = appE (conE 'S) (nat (n - 1))
现在我们可以编写
compBinRel=compose$(nat 2)not
,这对于较大的数字来说更令人愉快。有些人可能会认为这是“作弊”,但当我们只是执行一点语法糖时,我认为没关系:

GHC 7.8 GHC 7.8的以下工作:

-- A lot more extensions.
{-# LANGUAGE DataKinds, FlexibleContexts, FlexibleInstances, GADTs, MultiParamTypeClasses, PolyKinds, TypeFamilies, TypeOperators, UndecidableInstances #-}

module Nats where

import GHC.TypeLits

data N = Z | S N

data P n = P

type family Index n where
    Index 0 = Z
    Index n = S (Index (n - 1))

-- Compose is defined using Z/S instead of 0, 1, ... in order to avoid overlapping.
class Compose n f r where
    type Return n f r
    type Replace n f r
    compose' :: P n -> (Return n f r -> r) -> f -> Replace n f r

instance Compose Z a b where
    type Return Z a b = a
    type Replace Z a b = b
    compose' _ f x = f x

instance Compose n f r => Compose (S n) (a -> f) r where
    type Return (S n) (a -> f) r = Return n f r
    type Replace (S n) (a -> f) r = a -> Replace n f r
    compose' x f g = compose' (prev x) f . g
      where
        prev :: P (S n) -> P n
        prev P = P

compose :: Compose (Index n) f r => P n -> (Return (Index n) f r -> r) -> f -> Replace (Index n) f r
compose x = compose' (convert x)
  where
    convert :: P n -> P (Index n)
    convert P = P

-- This does not type check without a signature due to the monomorphism restriction.
compBinRel :: (a -> a -> Bool) -> (a -> a -> Bool)
compBinRel = compose (P::P 2) not

-- This is an example where we compose over higher order functions.
-- Think of it as composing (a -> (b -> c)) and ((b -> c) -> c).
-- This will not typecheck without signatures, despite the fact that it has arguments.
-- However, it will if we use the first solution.
appSnd :: b -> (a -> b -> c) -> a -> c
appSnd x f = compose (P::P 1) ($ x) f
然而,正如在源代码中注释的那样,这种实现有一些缺点

我尝试(但失败)使用封闭类型族自动推断合成索引。有可能推断出这样的高阶函数:

-- Given r and f, where f = x1 -> x2 -> ... -> xN -> r, Infer r f returns N.
type family Infer r f where
    Infer r r = Zero
    Infer r (a -> f) = Succ (Infer r f)
但是,
expert
不适用于具有多态参数的高阶函数。例如:

ghci> :kind! forall a b. Infer a (b -> a)
forall a b. Infer a (b -> a) :: *
= forall a b. Infer a (b -> a)

GHC无法展开
推断a(b->a)
,因为它在匹配闭合族实例时不执行发生检查。GHC不会匹配第二种情况下的
推断
,因为
a
b
被实例化,使得
a
b->a
相结合。不幸的是,在当前发布的GHC版本(GHC 7.6.3)中,原则上无法回答您的问题因为在最近的消息中指出了一致性问题


尽管类型级别的数字看起来像数字,但不能保证它们的行为完全像数字(而且它们也不是)。我已经看到Iavor Diatchki和同事在GHC中实现了正确的类型级算法(这与作为后端使用的SMT解算器一样可靠——也就是说,我们可以信任它)。在该版本发布之前,最好避免使用类型级别的数字文字,无论它们看起来多么可爱。

GHC 7.8版本相当不错。如果使用
{-#LANGUAGE ScopedTypeVariables}
,那么在第二个实例中是否可以将
(prev n)
替换为
(P::P(n-1))
。我之所以写
prev
,是因为我懒得滚动到文件的顶部并添加pragma:)好的,所以我想这使7.8版本成为我所希望的好版本。还有一件事:假设
(0-1)~0
,这7.8个实例似乎是重叠的。那么,您是否使用了
{-#语言重叠实例{-}
?更新很有趣!我最近在7.6.3中编写了一些类似的代码——用于计算函数的参数和返回类型,以及替换返回类型——但这只适用于以monad结尾的函数类型。由于7.6.3中没有封闭类型族,且实例是有序的,因此似乎无法识别基本情况(重叠的
Arity
实例之所以有效,是因为它们是有序的?)。但后来我意识到:基本情况不一定定义得很好:您可能希望使用更高阶的函数进行组合!因此,仍然需要
compose'
。是的,
Arity
可以工作,因为重叠的实例是有序的。而
Inst
是一个输入错误:)为了回答隐藏在评论中的问题:
2::Nat
不会进行类型检查,因为GHC只使用
fromInteger
来创建值级别的数字,而不是类型级别的数字。我看到了您链接到的电子邮件中的不一致性(“Singleton”不是Singleton),但我不明白这和我的问题有什么直接关系。