7

次のコードがあるとしましょう

type IsTall = Bool
type IsAlive = Bool

is_short_alive_person is_tall is_alive = (not is_tall) && is_alive

後で、私は次のものを持っているとしましょう

a :: IsAlive
a = False

b :: IsTall
b = True

そして、次のように呼び出して、2 つの引数を間違った方法で取得します。

is_short_alive_person a b

残念ながら、これは正常にコンパイルされ、実行時に、背の低い生きている人の代わりに、背の高い死んだ人が代わりに見つかります。

上記の例をコンパイルしないでください。

私の最初の試みは:

newtype IsAlive = IsAlive Bool
newtype IsTall = IsTall Bool

しかし、私は何かをすることはできません。

switch_height :: IsTall -> IsTall
switch_height h = not h

nots では定義されていません。sのみIsTallですBool

Bools を常に明示的に抽出することはできますが、それでは目的が大きく損なわれます。

基本的に、明示的なキャストなしではs およびs と相互作用しないことを除いて、 sが s と同じようにIsTall他の s と相互作用するようにします。IsTallBoolBoolIsAlive

これを達成するための最良の方法は何ですか。


ps GHCで行うことで、数字でこれを達成したと思います:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype UserID = UserID Int deriving (Eq, Ord, Num)
newtype GroupID = GroupID Int deriving (Eq, Ord, Num)

(つまり、UserID と GroupID は相互作用してはなりません)

しかし、私は s でこれを行うことができないようですBool(Bool の導出は機能しません)。とにかく、上記が最善のアプローチであるかどうかさえわかりません。

4

3 に答える 3

11

データ型を少し変更すると、それを Functor のインスタンスにすることができ、その後 fmap を使用して Boolean の操作を行うことができます

import Control.Applicative

newtype IsAliveBase a = IsAlive a 
newtype IsTallBase a = IsTall a 

type IsAlive = IsAliveBase Bool
type IsTall = IsTallBase Bool

instance Functor IsAliveBase where
    fmap f (IsAlive b) = IsAlive (f b)

instance Functor IsTallBase where
    fmap f (IsTall b) = IsTall (f b)

switch_height :: IsTall -> IsTall 
switch_height h = not <$> h -- or fmap not h

- 編集

&& のような操作では、それを Applicative のインスタンスにすることができます

instance Applicative IsAliveBase where
    pure = IsAlive
    (IsAlive f) <*> (IsAlive x) = IsAlive (f x)

そして、liftA2 を使用して (&&) を実行できます

例:

*Main> let h = IsAlive True
*Main> liftA2 (&&) h h 
IsAlive True

これについて詳しくはhttp://en.wikibooks.org/wiki/Haskell/Applicative_Functorsで読むことができます

于 2012-05-11T03:33:44.013 に答える
9

あなたのオプションは、次のような代数データ型を定義することです

data Height = Tall | Short
data Wiggliness = Alive | Dead

&&&または、 、|||、などの新しい演算子を定義complementして、選択した型でそれらをオーバーロードします。ただし、オーバーロードしても、それらを使用することはできませんif

とにかく、高さのブール演算が意味があるかどうかはわかりません。「背が高くて背が低いと背が低い」が「背が高くても背が低くても背が高い」という結論をどのように正当化しますか?

接続詞に別の名前を付けて、オーバーロードできるようにすることをお勧めします。

PS Haskell は常に新しい機能を追加しているので、私が言える最善のことは、オーバーロードできるかどうかifはわからないということです。Haskell について「これこれしかできない」と言うのは常に危険です...

于 2012-05-11T03:30:53.767 に答える
8

との値newtypeで使用するブール関数を非表示にするプレリュードをインポートする場合は、sとクラスを使用してこれにいくらかの方法を得ることができます。ブール関数をクラスのメソッドとして再定義し、そのために、、、およびタイプの3つすべてのインスタンスを作成します。使用する場合は、ラッピング/アンラッピングボイラープレートを手作業で作成しなくても、インスタンスを取得できます。IsTallIsAliveBoolIsTallIsAliveGeneralizedNewtypeDerivingIsTallIsAlive

これが私が実際にghciで試したスクリプトの例です:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import Prelude hiding ((&&), (||), not)
import qualified Prelude

class Boolish a where
    (&&) :: a -> a -> a
    (||) :: a -> a -> a
    not :: a -> a

instance Boolish Bool where
    (&&) = (Prelude.&&)
    (||) = (Prelude.||)
    not = Prelude.not

newtype IsTall = IsTall Bool
    deriving (Eq, Ord, Show, Boolish)

newtype IsAlive = IsAlive Bool
    deriving (Eq, Ord, Show, Boolish)

これで、、、、および3つのタイプのいずれかの値を使用できますが、一緒に使用することはでき&&ません。また、これらは別々のタイプであるため、関数のシグネチャで3つのうちどれを受け入れるかを制限できるようになりました。||not

他のモジュールで定義されている高階関数は、次のようにこれで正常に機能します。

*Main> map not [IsTall True, IsTall False]
[IsTall False,IsTall True]

ただし、他のモジュールはブール関数のPreludeバージョンを引き続き使用するため、IsTallを期待する他の場所で定義された他の関数にを渡すことはできません。Boolのような言語構造if ... then ... else ...も依然として問題になります(ただし、ノーマン・ラムゼーの回答に対するhammarのコメントによると、これは別のGHC拡張機能で修正できます)。そのような問題を軽減するためtoBoolに、通常のに一律に変換するのに役立つメソッドをそのクラスに追加したいと思います。Bool

于 2012-05-11T05:13:28.327 に答える