f#:在(归纳)类型中编码偶数和奇数?

时间:2015-12-14 03:37:42

标签: types f# type-theory

我一直在阅读Practical Foundations for Programming Languages,并发现迭代和同时归纳定义很有趣。我能够非常轻松地编码我发现的偶数和奇数函数的相互递归版本online

let rec even = function
| 0 -> true
| n -> odd(n-1)
and odd = function
  | 0 -> false
  | n -> even(n-1)

printfn "%i is even? %b" 2 (even 2)
printfn "%i is odd? %b" 2 (odd 2)

但如果我能在类型级别而不是通过函数执行此操作,那么我(我是F#newb)就不那么清楚了。我已经在F#中看到了Peano数字的实现,所以我觉得这应该是可能的。

2 个答案:

答案 0 :(得分:4)

这是黑魔法:

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

基于this implementation of Peano numbers并使用运算符来避免手写约束,!.代表奇数,!!代表偶数。

// Test
let N1 = Succ Zero
let N2 = Succ N1
let N3 = Succ N2
let N4 = Succ N3

isOdd N3       // val it : Yes = Yes
isEven N3      // val it : No = No

// Test at type-level
let inline doSomeOddStuff (x: ^t when ^t : (static member (!.) : ^t -> Yes)) =        
    ()

let x = doSomeOddStuff N3
let y = doSomeOddStuff N4   // Doesn't compile

我使用运算符来展示从价值级解决方案到类型级解决方案的容易程度。然后你可以继续使用静态约束来编写相同的内容,为了完整性,这里是那个版本:

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)

它更冗长,但在intellisense上读得更好,例如受约束的函数会读取:

 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

所以在这里你可以得到一个类型的结果,也可以得到该类型的DU值。

答案 1 :(得分:2)

这不是一个理想的设置,因为它们是 // sorting array if(wordList.size()>0){ String alphaList[] = new String[wordList.size()]; //convert list to String array alphaList= wordList.toArray(alphaList); //sorting Arrays.sort(alphaList); } ........ // let us print all the elements available in wordList if(wordList.size()>0){ for (String word: alphaList) { System.out.println("word= " + word); } } 的两个独立编码,但它已经基本了解它是如何工作的:

succ