2016-12-02 8 views
10

Scalaの記事で本当に興味深い例が見つかりました。私はHaskellでどのようにエンコードされているのだろうかと思っています。Scala型のサンプルをHaskellに翻訳する

trait Status 
trait Open extends Status 
trait Closed extends Status 

trait Door[S <: Status] 
object Door { 
    def apply[S <: Status] = new Door[S] {} 

    def open[S <: Closed](d: Door[S]) = Door[Open] 
    def close[S <: Open](d: Door[S]) = Door[Closed] 
} 

val closedDoor = Door[Closed] 
val openDoor = Door.open(closedDoor) 
val closedAgainDoor = Door.close(openDoor) 

//val closedClosedDoor = Door.close(closedDoor) // fails to compile 
//val openOpenDoor = Door.open(openDoor) // fails to compile 

このサンプルでは、​​あなただけの閉じDoorを開き、唯一のオープンDoorを閉じることができるタイプのレベルで符号化します。単純なデータ型を使用していましたが、意図したとおりに動作しません。私の最初の試み:私は、open関数で非網羅的なパターンを訴えたcloseClosedDoorまたはopenOpenedDoorを印刷しようとしない限り、

data Status = Open | Closed deriving (Show) 
data Door = Door Status deriving (Show) 

open :: Door -> Door 
open (Door Closed) = Door Open 

close :: Door -> Door 
close (Door Open) = Door Closed 

main = do 
    let closedDoor = (Door Closed) 
    let openDoor = open closedDoor 
    let closedAgainDoor = close openDoor 
    let closeClosedDoor = close closedDoor 
    let openOpenedDoor = open openDoor 
    print closedAgainDoor 

これは実際にある、(コンパイル明らかに)

GADTのタイプファミリーがこの作業を達成できるかどうかを判断しようとしていますが、まだどのように把握できていません。

アイデア?

答えて

18

bheklilrの答えに加えて、あなたはScalaの例にも、近づくとDataKinds使用

Door String 

のような無意味なタイプを除外するために、いくつかのタイプの拡張機能を使用することができ、あなたは効果的からファントムタイプを許可しない可能性がありStatus以外のものです。

{-# LANGUAGE DataKinds #-} 

data Door (status :: Status) = Door 
data Status = Open | Closed 

open :: Door Closed -> Door Open 
open _ = Door 

close :: Door Open -> Door Closed 
close _ = Door 

その後、型家族で、私たちも、それが「トグル」ドアに何を意味するかを定義することができ終値は、ためにGADTを使用することがよりよいかもしれ思ったよう

{-# LANGUAGE TypeFamilies #-} 

type family Toggle (s :: Status) where 
    Toggle Open = Closed 
    Toggle Closed = Open 

toggle :: Door s -> Door (Toggle s) 
toggle Door = Door 

Door - ちょうど2つの異なるコンストラクター名があります。私は個人的にこれがより良いと思う。

{-# LANGUAGE GADTs, DataKinds, TypeFamilies #-} 

data Door (status :: Status) where 
    OpenDoor :: Door Open 
    ClosedDoor :: Door Closed 

open :: Door Closed -> Door Open 
open _ = OpenDoor 

close :: Door Open -> Door Closed 
close _ = ClosedDoor 

toggle :: Door s -> Door (Toggle s) 
toggle OpenDoor = ClosedDoor 
toggle ClosedDoor = OpenDoor 
+1

あなたは 'DataKinds'答えに私を打つ。最初にコンパイルをチェックせずに投稿したいとは思っていませんでしたが、最初に 'stack setup'を実行しなければなりませんでした。私は 'TypeFamilies'で' toggle'アイデアが好きですが、それについては考えていませんでした。 – bheklilr

+0

ニース、私は自分自身に来て、まだ言語の高度な機能を学んでいるとは思わない。 –

+0

より良い推論のために、 'toggle ::(s〜Toggle t、t〜Toggle s)=> Door s - > Do t 'です。または 'class Toggled s t | s - > t、t - > sはトグル::ドアs - >ドアt'です。あなたはおそらくGHC 8用の注入型のファミリーを使用することができますが、まだそれらを購入しません。 – dfeuer

10

私は今、検討するいずれの場合は、状態自体は、Scalaの例のように、タイプでエンコードされ、そこではありません

data Open = Open deriving (Show) 
data Closed = Closed deriving (Show) 
data Door door_state = Door deriving (Show) 

open :: Door Closed -> Door Open 
open _ = Door 

close :: Door Open -> Door Closed 
close _ = Door 

ような何かをするだろう。

関連する問題