For Development HEAD DRAFTSearch (procedure/syntax/module):

11.42 srfi.189 - MaybeとEither、オプショナルなコンテナ型

Module: srfi.189

MaybeとEitherは、任意個の値を「ラップ」する変更不可なコンテナ型です。 Maybeは値が「有効な値が無い」という状況で使えます (これは「ゼロ個の値を生成する」とは異なります)。 Eitherは「正常動作の時の値」と「エラー時の値」を区別したい状況で使えます。

Haskellのような関数型言語に親しんでいるなら、これらの型については既に ご存知でしょう。これらの型はモナドで使うととても便利です。 例えば、処理の連なりをまるで失敗が起きないかのように書きながら、 もし失敗が起きたら自動的に残りの処理がキャンセルされて失敗情報が帰ってくる、 というふうにできます。

Maybeは2つの型、JustとNothingのユニオン型です。 Justは有効な値(複数可)をラップし、Nothingは「有効な値がない」ことを示します。 Eitherは2つの型、RightとLeftのユニオン型です。 Rightは有効な値(複数可)をラップしLeftはエラー時の情報をラップします。

Schemeには多値があるので、他の言語と違ってJust、Right、Leftは 任意個の値を持てることに注意してください。 例えば2つの値を返す手続きfをMaybeモナドの圏に持ち上げた手続きmfは、 2つの値を持つJustNothingを返すようになります。 ほとんどの場合は単一の値を扱うことになるでしょうが、 リファレンスを読む際には多値の場合の可能性を頭に置いておいてください。


11.42.1 型と述語

Class: <maybe>
Class: <just>
Class: <nothing>

{srfi.189} Maybe型のクラスです。<just><nothing><maybe>のサブクラスです。 <just>のインスタンスは任意個の値(ペイロード)を保持できます。 <nothing>のインスタンスは値を保持しません。

<maybe>クラス自体は抽象クラスで、それ自身のインスタンスは作りません。 <just><nothing>のインスタンスはそれぞれ コンストラクタjustおよびnothingで作ります。

Class: <either>
Class: <right>
Class: <left>

{srfi.189} Either型のクラスです。<right><left><either>のサブクラスです。 どちらのインスタンスは任意個の値(ペイロード)を保持できます。 慣習として、<right>は正常な計算結果を運ぶのに使われ、 <left>は異常が生じた時に何が異常かの情報を運ぶのに使われます。

<either>クラス自体は抽象クラスで、それ自身のインスタンスは作りません。 <right><left>のインスタンスはそれぞれ コンストラクタrightおよびleftで作ります。

Function: maybe? obj
Function: just? obj
Function: nothing? obj

[SRFI-189]{srfi.189} 型検査述語です。 それぞれ、objがMaybe, Just, Nothingである場合に#tを、 そうでなければ#fを返します。

Function: either? obj
Function: right? obj
Function: left? obj

[SRFI-189]{srfi.189} 型検査述語です。 それぞれ、objがEither, Right, Leftである場合に#tを、 そうでなければ#fを返します。

Function: maybe= elt= maybe1 maybe …

[SRFI-189]{srfi.189} Maybeについての等価判定述語です。NothingはNothingのみと等価であり、 Justはその対応するペイロード同士が全てelt=で比較して等価な場合にのみ 等価です。

Function: either= elt= either1 either …

[SRFI-189]{srfi.189} Eitherについての等価判定述語です。 Rightは同じペイロードを持つRightのみと、 Leftは同じペイロードを持つLeftのみと等価です。 ペイロード同士はelt=で比較されます。


11.42.2 コンストラクタ

Function: just obj …

[SRFI-189]{srfi.189} obj …をペイロードに持つJustのインスタンスを返します。

Function: nothing

[SRFI-189]{srfi.189} Nothingのインスタンスを返します。

Function: right obj …
Function: left obj …

[SRFI-189]{srfi.189} obj …をペイロードに持つ、それぞれRightとLeftのインスタンスを返します。

Function: list->just objs
Function: list->right objs
Function: list->left objs

[SRFI-189]{srfi.189} リストで与えられるオブジェクトをペイロードに持つ、Just、Right、Left のインスタンスをそれぞれ返します。

Function: maybe->either maybe obj …

[SRFI-189]{srfi.189} maybe引数はMaybeでなければなりません。 引数がJustの場合は。同じペイロードを持つRightが、 Nothingの場合は、obj …をペイロードに持つLeftが返されます。

Function: either->maybe either

[SRFI-189]{srfi.189} either引数はEitherでなければなりません。 引数がRightの場合は。同じペイロードを持つJustが、 Leftの場合はNothingが返されます。

Function: either-swap either

[SRFI-189]{srfi.189} either引数はEitherでなければなりません。 引数がRightの場合は。同じペイロードを持つLeftが、 Leftの場合は同じペイロードを持つRightが返されます。、


11.42.3 アクセサ

Function: maybe-ref maybe failure :optional success

[SRFI-189]{srfi.189} maybe引数はMaybeでなければなりません。 それがNothingなら、手続きfailureが引数なしで末尾呼び出しされます。 Justの場合は手続きsuccessが、Justのペイロードを引数として末尾呼び出しされます。 successが省略された場合はvaluesが使われます。

(maybe-ref (just 1 2) (^[] (error 'huh?)) +)
  ⇒ 3
Function: either-ref either failure :optional success

[SRFI-189]{srfi.189} either引数はEitherでなければなりません。 それがLeftなら、手続きfailureが、 Rightの場合は手続きsuccessがペイロードを引数として末尾呼び出しされます。 successが省略された場合はvaluesが使われます。

Function: maybe-ref/default maybe default …

[SRFI-189]{srfi.189} maybe引数はMaybeでなければなりません。 それがNothingなら、default …が値として返されます。 Justの場合はそのペイロードが値として返されます。

(maybe-ref/default maybe obj ...)
 ≡ (maybe-ref maybe (^[] (values obj ...)))
Function: either-ref/default maybe default …

[SRFI-189]{srfi.189} either引数はEitherでなければなりません。 それがLeftなら、そのペイロードは捨てられ、 default …が値として返されます。 Rightの場合はそのペイロードが値として返されます。

(either-ref/default either obj ...)
 ≡ (either-ref either (^ _ (values obj ...)))
Function: maybe-join maybe

[SRFI-189]{srfi.189} maybeがNothingならそのまま返されます。 それがJustで、一つだけの値を持ち、その値がMaybeならそれが返されます。 それ以外の場合はエラーが投げられます。

Function: either-join either

[SRFI-189]{srfi.189} eitherがLeftならそのまま返されます。 それがRightで、一つだけの値を持ち、その値がEitherならそれが返されます。 それ以外の場合はエラーが投げられます。

Function: maybe-bind maybe mproc mproc2 …

[SRFI-189]{srfi.189} モナドのbindです。maybeはMaybeでなければならず、 mproc mproc2 …のそれぞれはMaybeを返す手続きでなければなりません。

maybeがNothingならそれがそのまま返されます。 Justの場合は、そのペイロードを引数としてmprocが呼ばれます。 mproc2 …があれば同様に操作が繰り返されます。

(maybe-bind m p p2)
  ≡ (maybe-bind (maybe-bind m p) p2)
Function: either-bind either mproc mproc2 …

[SRFI-189]{srfi.189} モナドのbindです。eitherはEithereでなければならず、 mproc mproc2 …のそれぞれはEitherを返す手続きでなければなりません。

eitherがLeftならそれがそのまま返されます。 Rightの場合は、そのペイロードを引数としてmprocが呼ばれます。 mproc2 …があれば同様に操作が繰り返されます。

(either-bind e p p2)
  ≡ (either-bind (either-bind e p) p2)
Function: maybe-compose mproc mproc2 …
Function: either-compose mproc mproc2 …

[SRFI-189]{srfi.189} mproc mproc2 … はそれぞれ、ゼロ個以上の引数を取り Maybe/Eitherを返す手続きでなければなりません。 返り値は、ゼロ個以上の引数を取りMaybe/Eitherを返す手続きになります。

作られた手続きが呼ばれると、まずmprocが呼ばれます。それがNothing/Left を返した場合、あるいはもうmprocが無い場合はそれがそのまま返されます。 Just/Rightの場合は、そのペイロードを引数として次のmprocが呼ばれ、 それが繰り返されます。

(maybe-bind m p p2 ...)
  ⇒ (maybe-ref m (^[] (nothing))
              (maybe-compose p p2 ...))

11.42.4 シーケンス操作

MaybeとEitherは、0個または1個の要素を持つシーケンスとみなせます。 この視点からMaybe/Eitherを扱う手続きがいくつかあります (註: この用途においては、複数のペイロードはひとまとめにして1要素と して扱われます。手続き呼び出しや手続きからのリターンが、いくつ引数や返り値が あってもワンステップと考えられるのと同様です)。

Function: maybe-length maybe
Function: either-length either

[SRFI-189]{srfi.189} maybe/eitherがNothing/Leftなら0を、 Just/Rightなら1を返します。 引数がMaybe/Eitherでなければエラーが投げられます。

Function: maybe-filter pred maybe
Function: either-filter pred either obj …

[SRFI-189]{srfi.189} maybe/eitherがNothing/Leftなら、 それぞれNothingもしくはobj …をペイロードに持つLeftを返します。 maybe/eitherがJust/Rightなら、そのペイロードを引数として predを呼び、それが真の値を返したら maybe/eitherがそのまま返されます。 そうでなければNothingもしくはobj …をペイロードに持つLeftが返されます。

Function: maybe-remove pred maybe
Function: either-remove pred either obj …

[SRFI-189]{srfi.189} predの意味が逆になることを除いて、 maybe-filter/either-filterと同じです。

Function: maybe-sequence mappable cmap :optional aggregator
Function: either-sequence mappable cmap :optional aggregator

[SRFI-189]{srfi.189} この手続きは、Maybe/Eitherのコレクションを、 コレクションのMaybe/Eitherへと変換します。 入力となるコレクションと出力となるコレクションの型は違っていても構いません。

これは、Haskell的なタイプシグネチャを使った方が(厳密ではありませんが) わかりやすいでしょう。 Container xを何らかの型xのオブジェクトのコレクション、 a*を任意の型の多値とします。

Mappable   = Container Maybe a*
CMap       = ((Maybe a* -> b) -> Container Maybe a* -> Container' b
Aggregator = a* -> b

maybe-sequence :: Mappable -> CMap -> Aggregator -> Container' b

入力コンテナの要素が全てJustであれば、 cmapは最初の引数である手続き入力のコンテナmappableの各要素に適用し、 結果を別のコンテナに集積します。mappableと型があってさえいれば、 各コンテナの型は任意です。 例えばmappableにMaybeのベクタ、cmapvector-mapを 渡すことができます。この場合、ContainerContainer'の型は どちらもベクタです。あるいは、mappableはMaybeのリストで、 cmap(cut map-to <string> <> <>)を渡すこともできます。 この場合、Containerはリスト、Container'は文字列となります。

a*bの型はaggregator手続きにより決定されます。 デフォルトの値はlistです。

(use gauche.sequence) ; generic map
(map-sequence (vector (just 'a 'b) (just 'c 'd 'e) (just 'f))
              map
              vector)
  ⇒ #<Just (#(a b) #(c d e) #(f))>

入力コンテナの中にNothingがあればそれがそのまま返されます。

either-sequenceの動作も、Just/NothingをRight/Leftに入れ替える だけで同様です。入力シーケンスにLeftが含まれていた場合は 最初のLeftがそのまま返されます。

Function: maybe-map proc maybe
Function: either-map proc either

[SRFI-189]{srfi.189} maybe/eitherがNothing/Leftならばそれがそのまま返されます。 Just/Rightである場合は、そのペイロードがprocに渡され、結果が Just/Rightにくるんで返されます。

(maybe-map quotient&remainder (just 18 4))
 ⇒ #<Just 4 2>
Function: maybe-for-each proc maybe
Function: either-for-each proc either

[SRFI-189]{srfi.189} maybe/eitherがNothing/Leftならば何もしません。 Just/Rightならば、そのペイロードを引数としてprocが呼ばれます。 procの結果は捨てられます。

Function: maybe-fold kons knil maybe
Function: either-fold kons knil either

[SRFI-189]{srfi.189} maybe/eitherがNothing/Leftならknilが返されます。 Just/Rightならそのペイロードおよびknilを引数としてkonsが呼ばれ、 その結果が返されます。

(maybe-fold vector 'z (just 'a 'b))
  ⇒ #(a b z)
Function: maybe-unfold p f g seed …
Function: either-unfold p f g seed …

[SRFI-189]{srfi.189} まず、終了述語pseed …を引数として呼ばれます。 それが真の値を返したら、Nothingおよびseed …をペイロードとする Leftがそれぞれ返されます。 そうでなければ次のシードを生成するgseed …を引数として 呼ばれます。gは引数と同じ数の値を返さねばなりません。 gの戻り値はpに渡され終了条件がチェックされます。 もしpが真の値を返さなかったらエラーが投げられます (シーケンスとしてのMaybe/Eitherはたかだか1個の要素しか持てないので)。 そうでなければ、seed …が値生成手続きfに渡され、 その結果がJust/Rightにラップされて返されます。


11.42.5 プロトコル変換

Function: maybe->list maybe

[SRFI-189]{srfi.189} maybeがJustならば、そのペイロードのリストが返されます。 Nothingならば空リストが返されます。 (戻り値が空リストになるのは、maybeがNothingである場合と ゼロ個のペイロードを持つJustである場合があります)

これは「シーケンスとしてのMaybe」をリストに変換するものではないことに 注意してください。シーケンスとして見た場合、Justはつねに一つだけの 要素を持ちます。ただ、その要素が多値である場合があります。 この手続きは概念的にはvalues->listに似ています。

Function: list->maybe lis

[SRFI-189]{srfi.189} lisが空リストならNothingが、そうでなければ lisの各要素をペイロードとして持つJustが返されます。

(list->maybe (maybe->list x))は恒等関数ではないことに 注意して下さい。xがゼロ個のペイロードを持つJustの場合、 この式はNothingを返します。

Function: either->list either

[SRFI-189]{srfi.189} maybeがRightならば、そのペイロードのリストが返されます。 Leftならば空リストが返されます。 (戻り値が空リストになるのは、eitherがLeftである場合と ゼロ個のペイロードを持つRightである場合があります)

Function: list->either lis obj …

[SRFI-189]{srfi.189} lisが空リストならobj …をペイロードとして持つLeftが、 そうでなければlisの各要素をペイロードとして持つRightが返されます。

Function: maybe->truth maybe

[SRFI-189]{srfi.189} maybeがNothingなら#fが、ペイロードとして値をひとつだけ持つJustなら その値が返されます。ペイロードに1つ以外の値を持つJustだった場合は エラーが投げられます。任意のペイロードを扱いたい場合は 下のmaybe->list-truthを使って下さい。

#fが帰って来た場合に、元がNothingだったのか#fを値として 持つJustだったのか区別できないことに注意してください。 この手続きは、Maybeインタフェースと、従来の「Maybe的な」インタフェースとの 橋渡しをします。例えばfindは述語を満たす要素が無かった場合に #fを返しますが、「述語を満たす要素」が#fである場合を扱えません。 その意味で「Maybe的な」インタフェースは不完全なんですが、それを期待している 従来のコードにMaybeを使った結果を返したい場合にこの手続きが使えます。

Function: truth->maybe obj

[SRFI-189]{srfi.189} If obj is #f, returns a Nothing. Otherwise, returns a Just with obj as its payload.

Note that (truth->maybe (maybe->truth x)) isn’t an identity mapping— if x is a Just wrapping #f, you’ll get a Nothing.

Function: either->truth either

[SRFI-189]{srfi.189} If either is a Left, #f is returned. Otherwise, it must be a Right with one value, and its value is returned.

If either is a Right and it doesn’t have exactly one value, an error is thrown. If you want to deal with arbitrary number of payload values, use either->list-truth.

Function: truth->either obj fail-obj …

[SRFI-189]{srfi.189} If obj is #f, returns a Left with fail-obj … is returned. Otherwise, returns a Right with obj as its payload.

Function: maybe->list-truth maybe

[SRFI-189]{srfi.189} Like maybe->list, it returns #f if maybe is a Nothing. If maybe is a Just, however, it returns a list of its payload values.

Function: list-truth->maybe lis-or-false

[SRFI-189]{srfi.189} The argument must be #f of a list. If it is #f, a Nothing is returned. If it is a list, a Just with elements of the list is returned.

(list-truth->maybe (maybe->list-truth x)) is an identity mapping.

Function: either->list-truth either

[SRFI-189]{srfi.189} Like either->list, it returns #f if either is a Left. If either is a Right, however, it returns a list of its payload values.

Function: list-truth->either lis-or-false fail-objs …

[SRFI-189]{srfi.189} The list-or-false argument must be #f of a list. If it is #f, a Left with fail-objs … is returned. If it is a list, a Right with elements of the list is returned.

Function: maybe->generation maybe

[SRFI-189]{srfi.189} If maybe is a Nothing, an EOF object is returned. Otherwise, it must be a Just with one value, and its value is returned. If maybe is a Just and it doesn’t have exactly one value, an error is thrown.

Function: generation->maybe obj

[SRFI-189]{srfi.189} If obj is an EOF value, a Nothing is returned. Otherwise, a Just wrapping obj is returned.

Function: either->generation either

[SRFI-189]{srfi.189} If either is a Left, an EOF object is returned. Otherwise, it must be a Right with one value, and its value is returned. If either is a Right and it doesn’t have exactly one value, an error is thrown.

Function: generation->either obj fail-objs …

[SRFI-189]{srfi.189} If obj is an EOF value, a Left with fail-objs … is returned. Otherwise, a Right wrapping obj is returned.

Function: maybe->values maybe

[SRFI-189]{srfi.189} If maybe is a Just, returns its payload as multiple values. If it is a Nothing, returns no values. (Note that a Just with zero values also returns no values.)

Function: values->maybe producer

[SRFI-189]{srfi.189} It first invokes a procedure producer with no values. If it returns zero values, a Nothing is returned; otherwise, a Just with those values are returned.

Function: either->values either

[SRFI-189]{srfi.189} If either is a Right, returns its payload as multiple values. If it is a Left, returns no values. (Note that a Right with zero values also returns no values.)

Function: values->either producer fail-obj …

[SRFI-189]{srfi.189} It first invokes a procedure producer with no values. If it returns zero values, a Left with fail-obj … as its payload is returned. If it returns one or more values, a Right with those values are returned.

Function: maybe->two-values maybe

[SRFI-189]{srfi.189} If maybe is a Just with exactly one value, the value and #t is returned. If maybe is a Nothing, two #f is returned. An error is thrown if maybe has a Just with zero or more than two values.

Function: two-values->maybe producer

[SRFI-189]{srfi.189} The inverse of maybe->two-values. A procedure producer is called with no arguments. It must return two values, a possible payload value, and a boolean. If the second value is true, a Just with the first value is returned. If the second value is #f, a Nothing is returned (the first return value is ignored).

Function: exception->either pred thunk

[SRFI-189]{srfi.189} A procedure thunk is called without argument, wrapped by an exception handler. If thunk raises a condition, it is examined by pred. If pred returns true on the condition, the exception is wrapped by a Left and returned. If pred returns #f, the exception is reraised. If no exception is raised, the result(s) of thunk is wrapped by a Right and returned.


11.42.6 Syntactic utilities

Macro: maybe-if mtest then else

[SRFI-189]{srfi.189} If the mtest expression yields a Just, evaluates then. If the mtest expression yeilds a Nothing, evaluates else. If the mtest expression doesn’t produce a Maybe, an error is thrown.

Macro: maybe-and maybe …
Macro: either-and either …

[SRFI-189]{srfi.189} Evaluates maybe/either from left to right, as far as each yields a Just/Right. If every expression yields a Just/Right, the last one is returned. If it encounters an expression that yields a Nothing/Left, it stops evaluating the rest of expressions and returns the Nothing/Left.

If expressions yield something other than Maybe/Either, an error is thrown.

Macro: maybe-or maybe …
Macro: either-or either …

[SRFI-189]{srfi.189} Evaluates maybe/either from left to right, as far as each yields a Nothing/Left. If it encounters an expression that yields a Just/Right, it stops evaluating the rest of expressions and returns it.

If expressions yield something other than Maybe/Either, an error is thrown.

Macro: maybe-let* ( claw … ) body …
Macro: either-let* ( claw … ) body …

[SRFI-189]{srfi.189} This is a Maybe/Either version of and-let*.

Each claw can be either one of the following forms:

identifier

The identifier’s value is taken. It must be a Maybe/an Either, or an error is signaled. If it is a Just/Right, evaluation proceeds to the next claw. If it is a Nothing/Left, evaluation stops and the value is returned immediately.

(identifier expression)

The expression is evaluated. It must yield a Maybe/an Either, or an error is signaled. If it is a Just/Right, identifier is bound to its payload, and the rest of claws and body are processed with the scope of identifier. If it is a Nothing/Left, evaluation stops and the value is returned immediately. An error is signaled if a Just/Right doesn’t carry exactly one value.

( expression )

The expression is evaluated. It must yield a Maybe/an Either, or an error is signaled. If it is a Just/Right, evaluation proceeds to the next claw. If it is a Nothing/Left, evaluation stops and the value is returned immediately.

After all claws are processed and none yields a Nothing/Left, body … are evaluated.

Macro: maybe-let*-values ( mv-claw … ) body …
Macro: either-let*-values ( mv-claw … ) body …

[SRFI-189]{srfi.189} Multi-value payload version of maybe-let*/either-let*.

Each claw can be either one of the following forms:

identifier

The identifier’s value is taken. It must be a Maybe/an Either, or an error is signaled. If it is a Just/Right, evaluation proceeds to the next claw. If it is a Nothing/Left, evaluation stops and the value is returned immediately.

(formals expression)

The formals is the same as the formals of the lambda form, that is, a proper or dotted list of identifiers.

The expression is evaluated. It must yield a Maybe/an Either, or an error is signaled. If it is a Just/Right, identifiers in the formals are bound with the payload of the Just/Right, and the rest of claws and body are processed with the scope of those identifiers. If it is a Nothing/Left, evaluation stops and the value is returned immediately. An error is signaled if the formals doesn’t match the payload of the Just/Right.

( expression )

The expression is evaluated. It must yield a Maybe/an Either, or an error is signaled. If it is a Just/Right, evaluation proceeds to the next claw. If it is a Nothing/Left, evaluation stops and the value is returned immediately.

After all claws are processed and none yields a Nothing/Left, body … are evaluated.

Macro: either-guard pred body …

[SRFI-189]{srfi.189} The body … is evaluated, and the value(s) it produces are wrapped in a Right and returned. If an exception occurs in body …, the thrown condition is passed to a predicate pred. If the condition satisfies the predicate, it is wrapped in a Left and returned. Otherwise, the condition is reraised with raise-continuable.


11.42.7 Trivalent logic

This section describes procedures that deal with trivalent logic—a value can be a false value (Just #f), a true value (Just with anything other than #f), and Nothing.

If any of the arguments is Nothing, the result becomes Nothing (except tri=?).

All the argument must be Maybe type, or an error is signalled.

Function: tri-not maybe

[SRFI-189]{srfi.189} Returns Just #t if maybe is trivalent-false, Just #f if maybe is triavlent-true, and Nothing if maybe is Nothing.

Function: try=? maybe …

[SRFI-189]{srfi.189} Returns Just #t if arguments are either all trivalent-true or all trivalent-false. Otherwise return Just #f. Note that if any of the argument is Nothing, the result is Just #f (even all arguments are Nothing).

Function: try-and maybe …

[SRFI-189]{srfi.189} Returns Just #t if all arguments are trivalent-true, Just #f if all arguments are Just but at least one of them is Just #f, and Nothing if any of the arguments is Nothing. If there’s no arguments, Just #t is returned.

This is not a shortcut operation like and.

Function: try-or maybe …

[SRFI-189]{srfi.189} Returns Just #f if all arguments are trivalent-false, Just #t if all arguments are Just but at least one of them is trivalent-true, and Nothing if any of the arguments is Nothing. If there’s no arguments, Just #f is returned.

This is not a shortcut operation like or.

Function: try-merge maybe …

[SRFI-189]{srfi.189} If all arguments are Nothing, Nothing is returned. Otherwise, first Just is returned.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT