创建具有有限多居民的Haskell类型

创建具有有限多居民的Haskell类型,haskell,types,Haskell,Types,根据“Haskell for a great good”,Bool的类型声明是 data Bool = True | False Int的类型声明可以被认为是 data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647 对于一些抽象代数应用程序,我想创建一个具有有限多个指定值的类似类型,例如,值可能是介于$0$和$n$之间的整数,对于某些$n$。尽管声明了Int的定义,但以下内容不起作用: da

根据“Haskell for a great good”,Bool的类型声明是

data Bool = True | False
Int的类型声明可以被认为是

data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
对于一些抽象代数应用程序,我想创建一个具有有限多个指定值的类似类型,例如,值可能是介于$0$和$n$之间的整数,对于某些$n$。尽管声明了Int的定义,但以下内容不起作用:

data F3 = 0 | 1 | 2
错误为“类型中的非法文字”。如何创建一个只有这些居民的类型?类似于:

data F a = (Int a) => [0..a]
那太棒了


另外,我是否可以创建一个枚举类型的所有有效值的函数,或返回值列表?

您可以使用空构造函数(如
True
False
Nothing
()
等)

要枚举所有有效值,我们只需导出
Enum
Bounded
,并让GHC为我们完成所有工作

enum :: (Bounded a, Enum a) => [a]
enum = [minBound .. maxBound]

λ. enum :: [F3]
[Zero,One,Two]
如果您想像实际的
Int
那样使用它们,您可以使用
fromnum::Enum a=>a->Int
,这将相当于

fromEnum Zero = 0
fromEnum One  = 1
fromEnum Two  = 2

不能在这样的新类型中使用整型文字。它们已被使用,因此它的语法无效

cdk的回答给出了一个更实际的场景,但我想我会给出一个示例,说明如何在Haskell中实现类似于上一个示例的内容

如果我们打开一些更有趣的扩展,我们可以说服GHC使用给定的有限数量的值创建一个类型。但是,我可能不建议在实际代码中这样做,因为GHC目前没有对这种依赖类型(如编程)的最佳支持。此外,不幸的是,这些值没有很好的定义名字。据我所知,没有一种方法可以给他们起好名字,比如
1,2,3…
(编辑:实际上,我们可以在这方面做一些改进,请参见第二个代码块)

它可以是这样的:

{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeOperators #-}

module Fin (Fin (..))
  where

import GHC.TypeLits -- We get Nat from here as well as the type-level `<=` and `+`.

-- This is a type parameterized by another type. The type that parameterizes (`Nat`)
-- behaves like natural numbers at a type level. The `Nat -> *` is the "kind" of the type
-- `Fin`. A kind is like a type for types. To put it another way, a kind is to a type what
-- a type is to a value. In this case, the type `Nat` has kind `Nat`.
data Fin :: Nat -> * where
  FZero :: Fin upperBound
  FSucc :: (1 <= upperBound) => Fin upperBound -> Fin (upperBound + 1)

-- This is ok, because we are using the "fourth" value in a type with five values.
fifthValue :: Fin 5
fifthValue = FSucc (FSucc (FSucc FZero))

-- This doesn't compile, because it tries to make something of type
-- `Fin 2` using a "3rd value".
--    thirdValue :: Fin 2
--    thirdValue = FSucc (FSucc FZero)
--
-- The only inhabitants of `FiniteList 2` are `FZero` and `FSucc FZero`

尽管如此,
Eq
Ord
的实例仍然会有问题。

对于
Int
的示例声明发生在理论世界中,
Int
类型尚未定义,因此“符号”也未定义“这些数字中有多少还没有意义。实际上它不起作用,因为实际上,
Int
类型确实存在,而且这些数字有意义,因此它们不能用作值构造函数。Haskell无法从语言中删除数字文本。我想你可能会发现相关的东西,比如你的上一个例子,你在寻找依赖类型,这在Haskell中是不存在的。依赖类型是依赖于值的类型。Haskell(目前)中只有伪依赖类型。在依赖类型系统中,您的最后一个示例有时称为
Fin
,表示“有限”。@Chuck:在您的链接中讨论的示例中,使用Peano算术定义类型,取值范围可能就是我想要的。但这是相当有限的。它只允许创建一个限制在自然数范围内的类型,而我最终希望创建一个值限制在任意集合内的类型。@DavidYoung:依赖类型,因为该类型依赖于$a$?依赖类型与接受类型参数的类型构造函数不同吗?值得注意的是,您可以(滥用)使用
实例Num F3获取实际数字文本,其中{fromInteger=toEnum.flip mod 3}
@jozefg,这对我来说似乎并不滥用,esp如果定义了其他
Num
方法,我建议使用这种有限类型的包。
{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeOperators #-}

module Fin (Fin (..))
  where

import GHC.TypeLits -- We get Nat from here as well as the type-level `<=` and `+`.

-- This is a type parameterized by another type. The type that parameterizes (`Nat`)
-- behaves like natural numbers at a type level. The `Nat -> *` is the "kind" of the type
-- `Fin`. A kind is like a type for types. To put it another way, a kind is to a type what
-- a type is to a value. In this case, the type `Nat` has kind `Nat`.
data Fin :: Nat -> * where
  FZero :: Fin upperBound
  FSucc :: (1 <= upperBound) => Fin upperBound -> Fin (upperBound + 1)

-- This is ok, because we are using the "fourth" value in a type with five values.
fifthValue :: Fin 5
fifthValue = FSucc (FSucc (FSucc FZero))

-- This doesn't compile, because it tries to make something of type
-- `Fin 2` using a "3rd value".
--    thirdValue :: Fin 2
--    thirdValue = FSucc (FSucc FZero)
--
-- The only inhabitants of `FiniteList 2` are `FZero` and `FSucc FZero`
{-# LANGUAGE DataKinds, KindSignatures, GADTs, TypeOperators, PolyKinds, TypeFamilies, UndecidableInstances #-}
import Data.Type.Equality
import GHC.TypeLits

type family (||) (a :: Bool) (b :: Bool) :: Bool where
  True  || x  = True
  False || x  = x

type family Elem (a :: k) (xs :: [k]) :: Bool where
  Elem x (y ': ys) = (x == y) || Elem x ys
  Elem x '[]       = False


data NamedFin :: [k] -> * where
  Val :: ((a `Elem` as) ~ True) => Proxy a -> NamedFin as

-- Countdown n makes a type level list of Nats.
-- So, for example, Countdown 3 is a shorthand for '[3, 2, 1]
type family Countdown (a :: Nat) :: [Nat] where
  Countdown 0 = '[]
  Countdown n = (n - 1) ': Countdown (n - 1)

data Proxy a = Proxy deriving Show

type Example = NamedFin (Countdown 5) -- This is the same as NamedFin '[5, 4, 3, 2, 1]

-- This compiles...
example :: Example
example = Val (Proxy :: Proxy 4)

-- ...but this doesn't
--     example2 :: Example
--     example2 = Val (Proxy :: Proxy 10)