2017-02-16 5 views
2

は、これらの2和タイプ可能な製品タイプのインスタンスを制限する方法はありますか?

data Currency = 
    | GBP 
    | EUR 
    | DKK 

data Country = 
    | DE 
    | AT 
    | DK 
    | UK 

と次の製品とさせて頂きますタイプ

type CC = (Country, Currency) 

今、これらすべての国が欧州連合(の一部であってもええ、3000年から親愛なるソフトウェア考古学 - イギリスは一度EUの一部でした;-))彼らは異なった通貨を持っています。だから私はCC

(DE, EUR) 
(AT, EUR) 
(UK, GBP) 
(DK, DKK) 

の可能な値を制限し、他のすべての組み合わせが発現可能ではないようにしたいと思います。
このようなことをタイプレベルで表現することは可能ですか?
それ以外の場合は、Haskellerにどのようにアプローチするのでしょうか?

+1

この種のタイプレベルマッピングはどのようなものを使用しますか?英国のように、静的ではない現実世界のデータを反映しています。 – chepner

+0

私はほとんどデータが静的ではないと主張します。あまりにも遠い未来に火星を解決しようとするなら、私たちの日付システムと私たちの時間システムはもはや働かないでしょう。当分の間、私は24/365を実装することに自信を持っています。そして、私は上記の静的テーブルを使用します。最終的には、構成可能なモジュールを実装したいと思っているならば、常にその判断を呼びますか? – robkuz

答えて

8

これは過剰である可能性がありますが、現在の状況によっては、GADTを使用することができます。これは、少なくともあなたの通貨にコンストラクタ情報がないという事実に大きく依存しています。

{-# LANGUAGE GADTs, DataKinds #-} 

data Currency = GBP | EUR | DKK 

data Country c where 
    DE :: Country EUR 
    AT :: Country EUR 
    UK :: Country GBP 
    DK :: Country DKK 

それとも、私が思うの変異体は、おそらくあまり有用ではなく、ユースケースなしの質問

{-# LANGUAGE GADTs, DataKinds #-} 

data Currency = GBP | EUR | DKK 
data Country = DE | AT | DK | UK 

data CountryCurrency country currency where 
    DECC :: CountryCurrency DE EUR 
    ATCC :: CountryCurrency AT EUR 
    UKCC :: CountryCurrency UK GBP 
    DKCC :: CountryCurrency DK DKK 

に近い、最善のアプローチが何であるかを言うのは難しいです。 :)

+0

'DE'、' AT'などの名前が重複しているため、2番目の実装はコンパイルされません。 –

+0

@DavidYoungありがとう!一定。 – Alec

1

[編集:私はOPがコンパイル時のチェックのためにタイプレベルの解決策を求めていたということを見落としていました。私の答えはそれに答えることはできませんが、同様の状況では一般的に良い代替手段になる可能性があります]

私はこれを行う慣用方法はsmart constructorsを使用すると信じています。一言で言えば、モジュールでデータ型を定義し、 "危険な"データコンストラクタ( "不正な組み合わせ"を許可するもの)をエクスポートしないでください。その代わりに正当な値だけを返す関数をエクスポートします。あなたの場合:

module Money (CountryCurrency 
      , Country (..) 
      , Currency (..) 
      , cc 
      ) where 

data Currency = GBP | EUR | DKK deriving Show 

data Country = DE | AT | DK | UK deriving Show 

data CountryCurrency = CC Country Currency deriving Show 

cc :: Country -> Currency -> CountryCurrency 
cc DE EUR = CC DE EUR 
cc AT EUR = CC AT EUR 
cc UK GBP = CC UK GBP 
cc DK DKK = CC DK DKK 
cc _ _ = error "invalid country/currency combination" 

ccはスマートコンストラクタです。

*Main> cc DE EUR 
CC DE EUR 

*Main> cc UK GBP 
CC UK GBP 

しかし、あなたは違法なものにすることはできません:あなたは、このように有効な組み合わせを作ることができます

最も重要な
*Main> cc UK EUR 
*** Exception: invalid country/currency combination 
CallStack (from HasCallStack): 
    error, called at ./Money.hs:18:10 in main:Money 

、あなたが直接データコンストラクタCC使用することはできません。

*Main> CC UK EUR 

<interactive>:26:1: error: 
    Data constructor not in scope: CC :: Country -> Currency -> t 
+0

私の目的は、コンパイル時のチェックをすることでした。 – robkuz

+0

@robkuzスマートコンストラクタのリンクには、それについても何か言いたいことがあります。多分それが助けになるでしょう。 –

+1

私は '国 - >通貨 - >多分CountryCurrency'が良いと思います。部分的な機能は悪いです。 – user2297560

4

をマイ競合は、このマッピングを表す製品タイプが間違った方法であり、そのタイプはその整合性をチェックするための良いツールではないということです。マッピングが変更された場合は、タイプも変更する必要があります。

各国は正確に1つの通貨を持ち、通貨は一意に決定されます。国。関数よりも、ペアのように聞こえます。

currency :: Country -> Currency 
currency DE = EUR 
currency AT = EUR 
currency UK = GBP 
currency DK = DKK 
関連する問題