Types f#:在(归纳)类型中编码偶数和奇数?
我一直在阅读,发现迭代和同步归纳定义很有趣。我能够很容易地对我发现的偶数和奇数函数的相互递归版本进行编码Types f#:在(归纳)类型中编码偶数和奇数?,types,f#,type-theory,Types,F#,Type Theory,我一直在阅读,发现迭代和同步归纳定义很有趣。我能够很容易地对我发现的偶数和奇数函数的相互递归版本进行编码 但我不太清楚(我是一个F#newb)是否可以在类型级别而不是通过函数来完成这项工作。我在F#中看到过Peano数字的实现,所以我觉得这应该是可能的。这不是一个理想的设置,因为它们是succ的两个独立编码,但它已经了解了它的基本原理: type Even= |Zero |Succ of Odd and Odd = |Succ_ of Even 以下是黑魔法: type Yes = Yes t
但我不太清楚(我是一个F#newb)是否可以在类型级别而不是通过函数来完成这项工作。我在F#中看到过Peano数字的实现,所以我觉得这应该是可能的。这不是一个理想的设置,因为它们是
succ
的两个独立编码,但它已经了解了它的基本原理:
type Even=
|Zero
|Succ of Odd
and Odd = |Succ_ of Even
以下是黑魔法:
type Yes = Yes
type No = No
type Zero = Zero with
static member (!!) Zero = Yes
static member (!.) Zero = No
type Succ<'a> = Succ of 'a with
static member inline (!!) (Succ a) = !. a
static member inline (!.) (Succ a) = !! a
let inline isEven x = !! x
let inline isOdd x = !. x
我使用运算符是为了说明从值级解决方案到类型级解决方案是多么容易。然后,您可以继续使用静态约束编写相同的代码,为完整起见,以下是该版本:
type Zero = Zero with
static member Even Zero = Yes
static member Odd Zero = No
type Succ<'a> = Succ of 'a with
static member inline Even (Succ a) : ^r = (^t : (static member Odd : ^t -> ^r) a)
static member inline Odd (Succ a) : ^r = (^t : (static member Even : ^t -> ^r) a)
let inline isEven x : ^r = (^t : (static member Even : ^t -> ^r) x)
let inline isOdd x : ^r = (^t : (static member Odd : ^t -> ^r) x)
更新
这里有一个可能更接近您想法的替代解决方案:
type Parity =
| Even
| Odd
type Even = Even with static member (!!) Even = Parity.Even
type Odd = Odd with static member (!!) Odd = Parity.Odd
type Zero = Zero with
static member (!!) Zero = Even
type Succ<'a> = Succ of 'a with
static member inline (!!) (Succ (Succ a)) = !! a
static member (!!) (Succ Zero) = Odd
let inline getParity x = !! x
let inline getParityAsValue x = !! (getParity x)
// Test
let N1 = Succ Zero
let N2 = Succ N1
let N3 = Succ N2
let N4 = Succ N3
getParity N3 // val it : Yes = Yes
getParity N4 // val it : No = No
getParityAsValue N3 // val it : Parity = Odd
getParityAsValue N4 // val it : Parity = Even
// Test at type-level
let inline doSomeOddStuff (x: ^t when ^t : (static member (!!) : ^t -> Odd)) =
()
let x = doSomeOddStuff N3
let y = doSomeOddStuff N4 // Doesn't compile
类型奇偶校验=
|甚至
|奇怪的
键入偶数=偶数与静态成员(!!)偶数=奇偶。偶数
输入奇数=奇数,带有静态成员(!!)奇数=奇数。奇数
键入Zero=Zero和
静态成员(!!)零=偶数
类型成功(奇数))=
()
设x=doSomeOddStuff N3
让y=doSomeOddStuff N4//不编译
所以在这里,你可以得到一个类型的结果,以及该类型的DU值。我相当确定进入依赖类型领域将是令人讨厌和难看的(Scala的
部分变形让我既害怕又敬畏),但可能可以用一些黑魔法和黑客来完成:)非常酷!你有没有理由选择“是”
/“否”,而不是将这些类型命名为“偶数”/“奇数”?我真的想象了一些更像约翰·帕尔默(John Palmer)下面的答案的东西,其中偶数和奇数是类型本身,而不是关于Peano数类型的方法。如果可能的话,是否可以将偶数和奇数类型组合在一个有区别的并集中,例如奇偶校验
?(在Boolean
中有一种True
/False
的镜像…)通常我使用Yes
和No
作为类型级别的布尔值,我不确定是否有某种约定,因为我以前见过它。您可以将它们重命名为偶数
和奇数
,但这不太一般。关于DU,请注意,如果将它们作为单个DU的一部分,它们将具有相同的类型,因此不会发生类型级别歧视。请参阅更新。我想它更接近你想要的。这里有类型级别和DU值级别。请注意,始终可以从(编译时)类型获取(运行时)值。事实恰恰相反,这正是我开始的方向。虽然我选择了EvenPlusOne
和OddPlusOne
而不是两个不同的Succ
。你能再充实一点吗?
type Zero = Zero with
static member Even Zero = Yes
static member Odd Zero = No
type Succ<'a> = Succ of 'a with
static member inline Even (Succ a) : ^r = (^t : (static member Odd : ^t -> ^r) a)
static member inline Odd (Succ a) : ^r = (^t : (static member Even : ^t -> ^r) a)
let inline isEven x : ^r = (^t : (static member Even : ^t -> ^r) x)
let inline isOdd x : ^r = (^t : (static member Odd : ^t -> ^r) x)
val inline doSomeOddStuff :
x: ^t -> unit when ^t : (static member Odd : ^t -> Yes)
type Parity =
| Even
| Odd
type Even = Even with static member (!!) Even = Parity.Even
type Odd = Odd with static member (!!) Odd = Parity.Odd
type Zero = Zero with
static member (!!) Zero = Even
type Succ<'a> = Succ of 'a with
static member inline (!!) (Succ (Succ a)) = !! a
static member (!!) (Succ Zero) = Odd
let inline getParity x = !! x
let inline getParityAsValue x = !! (getParity x)
// Test
let N1 = Succ Zero
let N2 = Succ N1
let N3 = Succ N2
let N4 = Succ N3
getParity N3 // val it : Yes = Yes
getParity N4 // val it : No = No
getParityAsValue N3 // val it : Parity = Odd
getParityAsValue N4 // val it : Parity = Even
// Test at type-level
let inline doSomeOddStuff (x: ^t when ^t : (static member (!!) : ^t -> Odd)) =
()
let x = doSomeOddStuff N3
let y = doSomeOddStuff N4 // Doesn't compile