2015-12-14 15 views
5

Czytałem Practical Foundations for Programming Languages i odkryłem, że interesujące są iteracyjne i symultaniczne definicje. Udało mi się dość łatwo zakodować wzajemnie rekursywną wersję parzystych i nieparzystych funkcji, które znalazłem online.f #: kodowanie parzystych i nieparzystych typów (indukcyjnych)?

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) 

Ale jest to mniej oczywiste dla mnie (jestem newb F #), czy mogę to zrobić na poziomie typu raczej niż za pomocą funkcji. Widziałem implementacje liczb Peano w F #, więc uważam, że to powinno być możliwe.

+0

Jestem prawie pewna, że ​​wejście na terytorium terytoriów zależnych będzie paskudne i brzydkie (części "bezkształtnych" Scali pozostawiają mnie zarówno przerażonymi, jak i przerażającymi), ale prawdopodobnie można to zrobić za pomocą czarnej magii i hackery:) –

Odpowiedz

4

oto czarno-magiczne:

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 

Na podstawie this implementation of Peano numbers i za pomocą operatorów, aby uniknąć ograniczeń zapisu ręcznie, !. oznacza nieparzyste i !! dla parzystych.

// 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 

Używam operatorów, aby pokazać, jak łatwo można przejść z rozwiązania poziomu wartości do rozwiązania typu poziomowego. Następnie można śmiało napisać to samo z ograniczeniami statycznymi, dla kompletności tutaj jest, że wersja:

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) 

To bardziej gadatliwy ale brzmi lepiej intellisense, na przykład funkcja ograniczona będzie czytać:

val inline doSomeOddStuff : 
    x: ^t -> unit when ^t : (static member Odd : ^t -> Yes) 

UPDATE

Oto alternatywne rozwiązanie, które może być bliżej do tego, co masz na myśli:

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 

Tutaj można uzyskać wynik jako typ, a także wartość DU z tego typu.

+0

Bardzo fajnie! Każdy powód, dla którego wybrałeś 'Yes' /' No' zamiast nazywać te typy 'Even' /' Odd'? Naprawdę wyobrażałem sobie coś bardziej podobnego do odpowiedzi Johna Palmera poniżej, gdzie parzyste i nieparzyste są same typy, a nie metody dotyczące typów liczb Peano. Jeśli to możliwe, czy możliwe jest łączenie typów parzystych i nieparzystych w dyskryminowanej unii, takich jak "parzystość"? (Rodzaj lustra 'True' /' False' w 'Boolean' ...) –

+0

Zwykle używam' 'Yes'' i' 'No'' jako poziomu typu Boolean, nie jestem pewien, czy istnieje rodzaj konwencji, ponieważ widziałem to już wcześniej. Możesz zmienić ich nazwę na '' Even'' i '' Odd'', ale to mniej ogólne. Jeśli chodzi o DU, należy pamiętać, że jeśli uczynisz je częścią jednego DU, będą one miały ten sam typ, więc nie dojdzie do dyskryminacji na poziomie typu. – Gustavo

+1

Zobacz aktualizację. Myślę, że jest bliższy temu, czego szukasz. Tam masz zarówno poziom typu, jak i poziom wartości DU. Zauważ, że zawsze można uzyskać wartość (czas wykonania) z typu (czasu kompilacji). Przeciwieństwo nie jest prawdą. – Gustavo

2

To nie jest całkiem idealne ustawienie jako ich są dwie oddzielne kodowania dla succ ale ma podstawowe pojęcia jak to może działać:

type Even= 
|Zero 
|Succ of Odd 
and Odd = |Succ_ of Even 
+0

To jest dokładnie ten sam kierunek, w którym zacząłem. Chociaż użyłem 'EvenPlusOne' i' OddPlusOne' zamiast dwóch różnych 'Succ'. Czy możesz trochę więcej? –

Powiązane problemy