私はHaskellに比較的慣れていないため、「Real World Haskell」を読み始めました。
私はたぶんタイプを偶然見つけ、たとえばJust 1
から実際の値を受け取る方法について質問があります。
私は次のコードを書きました:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = 0
これは私が使用するとうまくいきます:
combine (Just 1) Nothing (Just 2)
しかし、たとえば1を文字列に変更しても機能しません。
理由はわかっていると思います:eliminate
は1つの型(この場合はInt
)を返す必要があるためです。しかし、少なくとも文字列(または多分あらゆる種類のタイプ)を処理するようにeliminate
をどのように変更できますか?
[著者から編集、6年後]これは不必要に長い回答であり、なぜそれが受け入れられたのかわかりません。最高の賛成投票で提案されているようにmaybe
またはData.Maybe.fromMaybe
を使用してください答え。以下は、実際的なアドバイスというよりは、思考実験の詳細です。
それで、あなたはたくさんの異なるタイプのために機能する関数を作成しようとしています。これはクラスを作る良い機会です。 JavaまたはC++でプログラミングした場合、Haskellのクラスは、これらの言語のインターフェースのようなものです。
class Nothingish a where
nada :: a
このクラスは値nada
を定義します。これは、クラスのNothing
に相当するものと見なされます。楽しい部分です。このクラスのインスタンスを作成します!
instance Nothingish (Maybe a) where
nada = Nothing
タイプMaybe a
の値の場合、Nothingに似た値はNothing
!です。これはすぐに奇妙な例になります。しかしその前に、このクラスのインスタンスもリストにしてみましょう。
instance Nothingish [a] where
nada = []
空のリストはNothingのようなものですよね?したがって、文字列(Charのリスト)の場合、空の文字列""
が返されます。
数値も簡単に実装できます。 0は明らかに数値の「無」を表すことをすでに示しました。
instance (Num a) => Nothingish a where
nada = 0
ファイルの先頭に特別な行を配置しない限り、これは実際には機能しません
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
または、コンパイルするときに、これらの言語プラグマのフラグを設定できます。それらについて心配しないでください。それらは、より多くのものを機能させる魔法にすぎません。
これで、このクラスとそのインスタンスができました...今度は、それらを使用するように関数を書き直しましょう!
eliminate :: (Nothingish a) => Maybe a -> a
eliminate (Just a) = a
eliminate Nothing = nada
0
をnada
に変更しただけで、残りは同じです。スピンしてみよう!
ghci> eliminate (Just 2)
2
ghci> eliminate (Just "foo")
"foo"
ghci> eliminate (Just (Just 3))
Just 3
ghci> eliminate (Just Nothing)
Nothing
ghci> :t eliminate
eliminate :: (Nothingish t) => Maybe t -> t
ghci> eliminate Nothing
error! blah blah blah...**Ambiguous type variable**
価値観などに最適です。 (Just Nothing)がNothingに変わることに注意してください。それは奇妙な例でした。とにかくeliminate Nothing
はどうですか?まあ、結果の型があいまいです。私たちが何を期待しているのかわかりません。だから私たちはそれが私たちが望むタイプを伝える必要があります。
ghci> eliminate Nothing :: Int
0
他のタイプで試してみてください。それぞれがnada
を取得することがわかります。したがって、この関数をcombine
関数で使用すると、次のようになります。
ghci> let combine a b c = (eliminate a, eliminate b, eliminate c)
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
(2,"foo",Just 3)
ghci> combine (Just 2) Nothing (Just 4)
error! blah blah Ambiguous Type blah blah
「Nothing」のタイプを示すか、期待する戻りのタイプを示す必要があることに注意してください。
ghci> combine (Just 2) (Nothing :: Maybe Int) (Just 4)
(2,0,4)
ghci> combine (Just 2) Nothing (Just 4) :: (Int, Int, Int)
(2,0,4)
または、型シグネチャをソースに明示的に置くことにより、関数が許可する型を制限することもできます。これは、関数の論理的な使用が、同じタイプのパラメーターでのみ使用される場合に意味があります。
combine :: (Nothingish a) => Maybe a -> Maybe a -> Maybe a -> (a,a,a)
combine a b c = (eliminate a, eliminate b, eliminate c)
今では、たぶん3つすべてが同じタイプである場合にのみ機能します。そうすれば、Nothing
が他の型と同じ型であると推測されます。
ghci> combine (Just 2) Nothing (Just 4)
(2,0,4)
あいまいさはありません。しかし、今は前と同じように、組み合わせるとエラーになります。
ghci> combine (Just 2) (Just "foo") (Just (Just 3))
error! blah blah Couldn't match expected type blah blah
blah blah blah against inferred type blah blah
ええと、それは十分に長く、誇張された答えでした。楽しい。
標準から Prelude
、
maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing = n
maybe _ f (Just x) = f x
デフォルト値と関数を指定して、Maybe
の値に関数を適用するか、デフォルト値を返します。
あなたのeliminate
はmaybe 0 id
、例:識別関数を適用するか、0を返します。
標準から Data.Maybe
、
fromJust :: Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x
これはpartial関数です(total関数ではなく、すべての入力に対して値を返します)。ただし、可能な場合は値を抽出します。
私もHaskellを初めて使用しているので、これがまだプラットフォームに存在するかどうかはわかりません(確かに存在します)が、存在する場合に値を取得する「取得またはその他」関数についてはどうですか?デフォルト?
getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d = d
これは私がこの質問に来たときに私が探していた答えです:
https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Maybe.html#v:fromJust
...そして同様に、Either
の場合:
https://hackage.haskell.org/package/one-unwrap-1.1/docs/Data-Either-Unwrap.html
それらは、コンテキストから値をアンラップする、私が自分で書いたであろう機能を提供します。
eliminate
関数の型シグネチャは次のとおりです。
eliminate :: Maybe Int -> Int
これは、Nothingに対して0を返し、コンパイラにa :: Int
eliminate
関数内。したがって、コンパイラはcombine
関数の型シグネチャを次のように推定します。
combine :: Maybe Int -> Maybe Int -> Maybe Int -> (Int, Int, Int)
文字列を渡しても機能しないのはそのためです。
次のように書いた場合:
combine a b c = (eliminate a, eliminate b, eliminate c)
where eliminate (Just a) = a
eliminate Nothing = undefined
次に、それは文字列または他のタイプで機能します。その理由は、undefined :: a
は、eliminate
をポリモーフィックにし、Int以外の型に適用できるようにします。
もちろん、それはコードの目的ではありません。つまり、結合関数を合計することです。
実際、いくつかのNothing引数へのcombine
の適用が成功しても(これはHaskellがデフォルトで遅延しているためです)、結果を評価しようとするとすぐに、undefined
は有用なものとして評価できないため、ランタイムエラーが発生します(簡単に言えば)。