首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何创建带约束的类型

如何创建带约束的类型
EN

Stack Overflow用户
提问于 2011-11-02 17:36:42
回答 5查看 3.2K关注 0票数 27

例如,我想创建一个整数三元组的类型MyType。但不只是三个整数的笛卡尔乘积,我希望类型表示所有(x,y,z),这样x + y + z = 5

我该怎么做?除了从z = 5 - x - y开始只使用(x, y)

同样的问题,如果我有三个构造器A, B, C,类型应该都是(A x, B y, C z),这样x + y + z = 5

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-11-02 17:50:50

我认为这里的诀窍是你不在类型级别强制它,你使用“智能构造函数”:即只允许通过生成这样的值的函数来创建这样的“元组”:

代码语言:javascript
运行
复制
module Test(MyType,x,y,z,createMyType) where

data MyType = MT { x :: Int, y :: Int, z :: Int }

createMyType :: Int -> Int -> MyType
createMyType myX myY = MT { x = myX, y = myY, z = 5 - myX - myY }

如果您希望生成所有可能的此类值,则可以编写一个函数来实现此目的,可以使用提供的或指定的界限。

可以很好地使用类型级别的教堂数字或一些类似的数字来强制创建这些数字,但对于您可能想要/需要的内容来说,这几乎肯定是太多的工作。

这可能不是您想要的(即“除了只使用(x,y),因为z=5-x- y"),但它比试图在类型级别上强制限制允许有效值更有意义。

类型可以确保值的正确“类型”(没有双关语);为了确保值的有效性,您可以隐藏构造函数,并且只允许通过经过批准的函数创建,以保证所需的任何不变量。

票数 25
EN

Stack Overflow用户

发布于 2011-11-03 00:49:48

是的,智能构造器或Agda在这里是可行的,但如果你真的想疯狂地使用“依赖”方法,在Haskell中:

代码语言:javascript
运行
复制
{-# LANGUAGE GADTs, TypeFamilies, RankNTypes, StandaloneDeriving, UndecidableInstances, TypeOperators #-}

data Z = Z
data S n = S n

data Nat n where
  Zero :: Nat Z
  Suc  :: Nat n -> Nat (S n)

deriving instance Show (Nat n)

type family (:+) a b :: *
type instance (:+) Z b = b
type instance (:+) (S a) b = S (a :+ b)

plus :: Nat x -> Nat y -> Nat (x :+ y)
plus Zero y = y
plus (Suc x) y = Suc (x `plus` y)

type family (:*) a b :: *
type instance (:*) Z b = Z
type instance (:*) (S a) b = b :+ (a :* b)

times :: Nat x -> Nat y -> Nat (x :* y)
times Zero y = Zero
times (Suc x) y = y `plus` (x `times` y)

data (:==) a b where
  Refl :: a :== a

deriving instance Show (a :== b)

cong :: a :== b -> f a :== f b
cong Refl = Refl

data Triple where
  Triple :: Nat x -> Nat y -> Nat z -> (z :== (x :+ y)) -> Triple

deriving instance Show Triple

-- Half a decision procedure
equal :: Nat x -> Nat y -> Maybe (x :== y)
equal Zero Zero = Just Refl
equal (Suc x) Zero = Nothing
equal Zero (Suc y) = Nothing
equal (Suc x) (Suc y) = cong `fmap` equal x y

triple' :: Nat x -> Nat y -> Nat z -> Maybe Triple
triple' x y z = fmap (Triple x y z) $ equal z (x `plus` y)

toNat :: (forall n. Nat n -> r) -> Integer -> r
toNat f n | n < 0 = error "why can't we have a natural type?"
toNat f 0 = f Zero
toNat f n = toNat (f . Suc) (n - 1)

triple :: Integer -> Integer -> Integer -> Maybe Triple
triple x y z = toNat (\x' -> toNat (\y' -> toNat (\z' -> triple' x' y' z') z) y) x

data Yatima where
  Yatima :: Nat x -> Nat y -> Nat z -> ((x :* x) :+ (y :* y) :+ (z :* z) :== S (S (S (S (S Z))))) -> Yatima

deriving instance Show Yatima

yatima' :: Nat x -> Nat y -> Nat z -> Maybe Yatima
yatima' x y z = 
  fmap (Yatima x y z) $ equal ((x `times` x) `plus` (y `times` y) `plus` (z `times` z)) (Suc (Suc (Suc (Suc (Suc Zero)))))

yatima :: Integer -> Integer -> Integer -> Maybe Yatima
yatima x y z = toNat (\x' -> toNat (\y' -> toNat (\z' -> yatima' x' y' z') z) y) x


{-
λ> triple 3 4 5
Nothing
λ> triple 3 4 7
Just (Triple (Suc (Suc (Suc Zero))) (Suc (Suc (Suc (Suc Zero)))) Refl (Suc (Suc (Suc (Suc (Suc (Suc (Suc Zero))))))))

λ> yatima 0 1 2 
Just (Yatima Zero (Suc Zero) (Suc (Suc Zero)) Refl)
λ> yatima 1 1 2 
Nothing
-}

然后,你的代码中有一个静态检查的不变量!除了你可以说谎..。

票数 21
EN

Stack Overflow用户

发布于 2014-03-17 01:31:05

通常的依赖类型方法是使用sigma (依赖产品)类型,例如在Agda中:

代码语言:javascript
运行
复制
open import Relation.Binary.PropositionalEquality (_≡_)
open import Data.Nat (ℕ; _+_)
open import Data.Product (Σ; ×; _,_)

FiveTriple : Set
FiveTriple = Σ (ℕ × ℕ × ℕ) (λ{ (x , y , z) → x + y + z ≡ 5 })

someFiveTriple : FiveTriple
someFiveTriple = (0 , 2 , 5) , refl

这就是为什么Σ通常被称为“存在的”类型:它允许您指定某些数据和有关该数据的某些属性。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7978191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档