38

私はHaskellに比較的慣れておらず、「RealWorldHaskell」を読み始めました。

たぶんタイプに出くわし、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をStringに変更すると、機能しません。

私は理由を知っていると思います:なぜならeliminate、1つのタイプ、この場合は。を返す必要があるからIntです。eliminateしかし、少なくとも文字列(またはおそらくすべての種類のタイプ)を処理するように変更するにはどうすればよいですか?

4

5 に答える 5

48

標準から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

これは部分関数です(関数ではなく、すべての入力に対して値を返します)が、可能な場合は値を抽出します。

于 2011-02-09T02:01:23.170 に答える
24

[著者からの編集、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-like値は、まあ、Nothing!これはすぐに奇妙な例になります。しかしその前に、リストをこのクラスのインスタンスにもしましょう。

instance Nothingish [a] where
    nada = []

空のリストは、何もないようなものですよね?したがって、文字列(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

まあ、それは十分に長くて誇張された答えだったと思います。楽しみ。

于 2011-02-09T05:08:20.540 に答える
4

私もHaskellを初めて使用するので、これがプラットフォームに存在するかどうかはまだわかりません(確かに存在します)が、値が存在する場合は「get or else」関数を使用して、それ以外の場合は戻り値を取得します。デフォルト?

getOrElse::Maybe a -> a -> a
getOrElse (Just v) d = v
getOrElse Nothing d  = d
于 2014-02-26T17:53:51.460 に答える
2

これは私がこの質問に来たときに私が探していた答えです:

https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-Maybe.html#v:fromJust

...そして同様にEither

https://hackage.haskell.org/package/ether-unwrap-1.1/docs/Data-Either-Unwrap.html

それらは、私が自分で書いたであろう関数を提供し、そのコンテキストから値をアンラップします。

于 2016-08-29T14:41:57.590 に答える
1

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

その場合、Stringまたはその他のタイプで機能します。undefined :: aその理由は、がeliminate多型になり、Int以外の型に適用できるという事実に依存しています。

もちろん、それはコードの目的ではありません。つまり、combine関数を合計することではありません。

確かに、combineいくつかのNothing引数への適用が成功したとしても(Haskellはデフォルトで怠惰であるため)、結果を評価しようとするとすぐに、undefined有用なものに評価できないため、ランタイムエラーが発生します(簡単に言えば)。

于 2011-02-09T12:50:03.320 に答える