创建具有有限多居民的Haskell类型
根据“Haskell for a great good”,Bool的类型声明是创建具有有限多居民的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
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)