根据“Haskell的好消息”,Bool的类型声明是
data Bool = True | False
并且Int的类型声明可以被认为是
data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
对于一些抽象代数应用程序,我想创建一个具有有限多个指定值的类似类型,例如,对于某些$ n $,值可以是$ 0 $和$ n $之间的整数。尽管声明了Int的定义,但以下方法不起作用:
data F3 = 0 | 1 | 2
错误"类型“非法文字”。如何创建一个这些是唯一居民的类型?类似的东西:
data F a = (Int a) => [0..a]
会非常棒。
另外,我可以创建一个枚举类型的所有有效值的函数,还是返回值列表?
答案 0 :(得分:9)
您可以使用无效构造函数(例如True
,False
,Nothing
,()
等)
data F3 = Zero | One | Two
deriving (Bounded, Enum, Show)
要枚举所有有效值,我们只需导出Enum
和Bounded
,让GHC为我们完成所有工作。
enum :: (Bounded a, Enum a) => [a]
enum = [minBound .. maxBound]
λ. enum :: [F3]
[Zero,One,Two]
如果您想使用实际Int
这些内容,可以使用等同于{/ p>的fromEnum :: Enum a => a -> Int
fromEnum Zero = 0
fromEnum One = 1
fromEnum Two = 2
答案 1 :(得分:5)
您不能在类似的新类型中使用整数文字。它们已被占用,因此它不是有效的语法。
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
之类的实例,您可能需要做更多棘手的事情(我甚至不确定是否可能。它可能需要新类型水平自然数定理证明他们正在加入GHC 7.10)。
我应该指出的另一件事是,Nat
种的类型被命名为1, 2, ...
。这是可以的,因为在类型级别(仅在值级别)没有任何其他名称使用这些名称。
编辑:
如果我们开启更多扩展,我们可以获得一个版本,您可以(几乎)直接指定值的名称:
{-# 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)
虽然Eq
和Ord
的实例仍然存在问题。