5057


Haskell で、ある再帰直和型 `T` に対し、 `T -> T` のような関数があり、既に実装されているとします。たとえば、

[code]

    data T = Z
           | One T
           | Two T T
           | Three T T T
           | ..  -- 沢山あります
    
    f :: T -> T
    f Z             = Z
    f (One t)       = One (f t)
    f (Two t1 t2)   = Two (f t2) (f t1)  -- 単純なトラバーサルではない
    f (Three Z Z Z) = error "Three 0s are illegal"
    f .. -- ずっと続きます
    
[/code]

一部のケースは、ありえない入力だということで、 `error` を使って例外を飛ばしています。

この関数を `T -> Maybe T` や `T -> Either String T`
のように型を変えて「行儀を良くして」例外を投げないようにしたいのですが、どのようにするのが一番良いのか、が質問です。

[code]

    f :: T -> Either String T
    f Z             = return Z
    f (One t)       = liftM One (f t)
    f (Two t1 t2)   = liftM2 Two (f t2) (f t1)
    f (Three Z Z Z) = fail "Three 0s are illegal"
    f ..
    
[/code]

などのようにモナドなり、アプリカティブファンクターなり、使用すると書けるのは判るのですが、コンストラクタ数が多い直和型の場合、追加コードを書いていて鬱々としてきます。

通常の Haskell では IO 関連だと例外は `catch` できるのですが、この場合、元の関数は IO を使いませんから、 IO
汚染させたくありません。非IO でも例外を catch できる言語では間違いなく例外を使う例なので当惑しています。

元のソースコードのをできるだけ変更せずに `Either`化や `Maybe`化したいのですが、 Haskell では無理なのでしょうか。


CC BY-SA 3.0
2015-01-22T04:24:50.150
5042
2015-01-23T03:31:31.520
2015-01-23T03:31:31.520
898
898
question
haskell
`error` を投げることがある巨大な再帰ケースをできるだけ簡単に `Maybe` 化する方法はありますか


Template Haskell(QuasiQuoter)が嫌いでなければ、それを実現するための糖衣があります。

[applicative-quoters](https://hackage.haskell.org/package/applicative-
quoters-0.1.0.8/docs/Control-Applicative-QQ-Idiom.html)パッケージで定義されているものを利用すると、

[code]

    f (Two t1 t2) = [i| Two (f t2) (f t1) |]
    
[/code]

のように、`[i| … |]`で囲むだけで、自動的に `Two <$> f t2 <*> f t1`
に変換してくれますが、ネストしている場合に対応していない点に注意が必要です。

もう一つの方法として、純粋な例外ハンドラを利用するという方法もあります。

[spoon](https://hackage.haskell.org/package/spoon/docs/Control-
Spoon.html)パッケージで提供されている`teaspoon :: a -> Maybe
a`は、例外が発生したとき`Nothing`、発生しなかった場合は`Just`で結果を返します。例外ハンドラをカスタマイズする`teaspoonWithHandles
:: Handles a -> a -> Maybe a`という関数も用意されています。

おそらく、後者が求めているのに近いと思います。参考になれば幸いです。


CC BY-SA 3.0
2015-01-22T06:29:50.107
5057
2015-01-22T06:37:16.410
2015-01-22T06:37:16.410
4978
2826
5042
answer