他人のHaskell日記 RSSフィード

Haskell初心者が、リハビリがてらに「ふつける」と「入門Haskell」片手に、試行錯誤するサイト。

2009-01-01

Arrowが分からない。  Arrowが分からない。 - 他人のHaskell日記 を含むブックマーク

If your application works fine with monads, you might as well stick with them.But if you're using a structure that's very like a monad,but isn't one,maybe it's an arrow.


(Monadを使ってアプリケーションがちゃんと動いているのなら、それを使い続けた方がよい。しかし、もし極めてモナドに似ていて、Monadでないようなものに出会ったとしたら、それは恐らくArrowだ。)

http://www.haskell.org/arrows/

<モナデッィクなパーサーの最適化をしていたらモナドのインターフェイスと互換性が無くなってしまった例をあげ、それは特別な例ではないと言う>


While arrows are a little less convininet to use than monads.they have significantly wider applicability.They can therefore be used to bring the benefits of monad like programming to a much wider class of application.


(Arrowの便利さは少しMonadに劣るが、使用可能範囲は広い。だからモナド的プログラミングの利点を、より広い種類のアプリケーションに持ち込める)

http://www.cs.chalmers.se/~rjmh/Papers/arrows.pdf

必要になるまでは、使わなくてもいいってことかな。

しかし気になる

なぜモナド なぜモナド - 他人のHaskell日記 を含むブックマーク

ここまで、コンビネータライブラリの基礎としてモナドが使うことができることを紹介した。

しかしながら、なぜ、モナドを使うべきなんだろうか?なぜモナドは今日のHaskellのプログラムに偏在するのだろうか?

ひとつの理由はもちろん、モナドを使うことがコードを劇的に単純化することだ。明示的な失敗のためのテストや明示的な入力の受け渡しをあちらこちらに書いたりするのは、symbol,>>=,returnを使うよりとても骨が折れるのは明らかだ。しかしながらこれは、コードをカプセル化するような、あらゆるコンビネータライブラリを使う利点であり、特にモナドを使うことは議論の範疇にない。モナドを使う他の理由は、コンビネータライブラリに設計の指針を与えることだ。しばしば適切なモナドを定義することから始めることは、よいスタートになる。例えば、パーサのライブラリに二つのパーサを連続で呼び出せるようなコンビネータを含める事は明白だ。しかしながら、コンビネータ二つのパーサの結果を扱うには様々なやりかたがある。あるパーサライブラリでは結果を一緒にペアにされるかもしれない。他のパーサでは、コンビネータが追加の引数として関数を取り、それを使って結果を結合するかもしれない。モナディックオペレータ>>=はより一般的な方法であり、これがあれば両方できるが、逆はそうではない。設計者は、パーサライブラリの基礎をモナドに置くことによって、ユーザにアドホックな他の選択肢よりも、より柔軟性を与えることができる。実際に、経験上モナドのインターフェイスがライブラリの利用者に凄い力を与えることを知っている。

他方、モナドのインターフェイスはコンビネータライブラリの実装に柔軟性を与える。非常に多くの使い方があるからだ。

我々は既に3つのモナドの例を見てきた。実際にはモナドトランスフォーマーを使えば、システマティックに無限の種類のモナドを構成できる。モナド設計のシステマティックなアプローチは実装者がコンビネータライブラリを基礎とする適切な方を探しやすくする。(以下略)

その他モナドに共通な関数が大量に使えることだとか云々。

http://www.cs.chalmers.se/~rjmh/Papers/arrows.pdf

圏論と抽象化  圏論と抽象化 - 他人のHaskell日記 を含むブックマーク

少ししか読んでないけれど、次の一節が印象に残った。

少し脱線して圏論の議題について語ろう。モナドの概念は圏論学者によって開発された。それは関数型言語への応用がついに発見されるより、遥か昔の事だった。圏論のような抽象的なものが、プログラミングのような具体的なものにとって有用だと判明するなんて不思議に思えるかもしれない。結局のところ、圏論は、その意味では抽象的すぎて不満足なものだ。それは「全ては定義である、定理は無い」という。もし十分な時間の観察をすれば、ほとんど全てが圏だと判明する。何かが圏である、ということは実際には何も言っていないに等しい。これは圏論のほとんどの概念についても真である。非常に多くの可能なインスタンス化があり、何かがモナドである、というのは殆ど何もいっていない。この極度の一般化は、初学者が圏論への入門に躓く理由となっている。しかし、それは驚くべきことではない。圏論というのは結局の所、万物の理論、つまり大量の異なった数学的な構造のフレームワークとなるべく開発されたのである。しかしながら何故、ある抽象的な理論が、プログラミングの役に立つなんて事があるのだろうか?

答えは単純だ。コンピュータ科学者として我々は抽象化を評価するのだ!。ソフトウェアのコンポーネントを設計するとき、実装は出来るだけ隠れていて欲しいと思うものだ。特定の実装を他の実装(同じ「概念」の他の多くの「インスタンス」)にいつでも起き換えられるようにしたいものだ。多くのプログラムのライブラリに一般的なインターフェイスを設計するとき、設計者が選択したインターフェイスを使って、幅広く多種類の実装がなされるようにするのは重要なことだ。それが我々が高く評価するモナドという概念の一般性であり、又、圏論が抽象的であるが故にその概念がプログラミングに有効な理由である。

ゆえに、以下に示すモナドの一般化が圏論と深い関係を持っていることは驚くようなことではない。しかしながら、その目的は実用的なものなのだと強調したい。「圏論を実装するため」の議論ではない。コンビネータライブラリを構造化するより一般的な手段を探すのが目的だ。それは我々にとって単純に幸運な事なのだ。数学者がほとんどの仕事をわれわれのために既にやってくれたのだから。

http://www.cs.chalmers.se/~rjmh/Papers/arrows.pdf

The Craft of Functional Programming 1章  The Craft of Functional Programming 1章 - 他人のHaskell日記 を含むブックマーク

関数型言語とOO言語の違いは、問題のモデル化するために提供するツールの差だよ。この差がモデル化の考え方を産み出しているよ。

  • 関数型プログラマは値の関係に注目するよ
  • OOプログラマはオブジェクトそのものに注目するよ。

型を与えることは、その使われかたについて多くの情報を提供するよ。

  • テストと証明
    • テストは、入力の一部のテストデータに対してしか動作を保障しないよ。
    • 証明なら、全ての入力に対しての動作を保障できるよ。
    • 高い信頼性が必要な分野では証明の出番。
    • しかしテストに意味がないわけではなく、テストと証明は相補的だよ。
  • ある関数とある関数の振舞いが等しいという「証明」は、transformに使えるよ。
    • これは言語の実装者にとって重要だけど、普通のソフトの開発でも重要だよ。


The Craft of Functional Programming 4章  The Craft of Functional Programming 4章 - 他人のHaskell日記 を含むブックマーク

設計
詳細なHaskellのコードを書く前のステージ
何をしようとしているか理解しているだろうか?
  • 単純な問題ですら、プログラムする前に考えるべきことがある。(仕様には漏れがある)
  • プログラムがどう振る舞うべきか、正しい答はない。プログラムを書くように要求した人と、プログラムを書く人自身が決める。(正しい仕様などは無い)
  • 問題を理解しているかを判断するために、いくつかの例を使って考えるとよい
  • このような問題は、実際にプログラムをはじめてからも何どもでてくる。
この時点で型について言えることがあるだろうか?

関数をどのように定義するかを考える前に型の事を考えよう。そのうち定義を書くときに型が正しさの手掛りとなる。

私が既に知っていることはなにか?その情報が使えないか?

問題を解決するのに使えるリソースについて知っておく必要がある。既に過去に書いた便利な定義や、言語がPreludeやライブラリで提供するものなど。

使えそうなfがある時

  • 新しい定義をfをモデルにして書く
  • 新しい定義をfを使って書く

という二つの方法がある。

問題を小さく分割できないか?

「もし、ある望んだ関数が存在したとしたら、それを使って問題を解決できるだろうか?」と考える。

「望んだ関数」が存在するかのようにしてトップレベルの関数を書き、「望んだ関数」を後に定義する。(トップダウン)

再帰

  • bottom-up表現は、基底ケースから次々と新しい値を作っていくのだと言っている。
  • top-down視点は評価されるゴールからはじめて、等式の単純化が基底ケースに辿りつくまでをみていく。

未定義ならerrorメッセージを書いておこう


fun (n-1)が既に存在したとしたら、fun n はそれを使ってどうかける?」とかんがえよう。

プログラムテスト

black box
仕様を知らずにテストをする
white box
仕様を知った上でテストをする
black box testing

入力を、関数が同じように振る舞うであろうグループ毎に分ける。

境界のような特別な例には特に注意を払う必要がある。典型的にはゼロとか。

テストはそれ自身では関数が正しいことを保障しない。

White box testing
  • 関数定義の全てのケースについてテストを行う。境界についてもそうである。
  • 再帰を使っていれば、0の場合、1の場合、一般の場合でテストを行う。

2008-12-29

Programming in Haskellの12章の自分なりのまとめ。 Programming in Haskellの12章の自分なりのまとめ。 - 他人のHaskell日記 を含むブックマーク

Introduction

状態のある言語では評価の順序が変わると大変な事になっちゃう。でも「純粋」な言語では、評価の順序は、引数が関数に適用されたときの返り値に、まったく影響を与えない。しかし、それでも評価の順序は意味があるんだよ。

Evalution Strategie

関数適用によって簡約できる「ひとつ以上の引数に適用されてる関数の形 (例:div 6 2)」を簡約可能式、略してredexと呼ぶ。簡約が式のサイズを変化させるとは限らない、けど実際は簡約で式のサイズが減るのはよくあること。

redexを簡約していく順番を決める戦略には二つある。

ただしHaskellのbuilt-inの関数 + *とかは、引数を先に評価する。(後で議論するけど)こういうの関数の性質を正格と呼ぶんだ。

lambda expression

Haskellではlambda式の中のredexに触れることは禁止されている。lambda式の中はブラックボックスで触れない。許されるlambda式への操作は、引数の適用だけ。適用して,中身を取り出してはじめて中のredexに触れる。

  • lambda式の中以外での最内評価をcall-by-valueと呼ぶ。
  • lambda式の中以外での最外評価をcall-by-nameと呼ぶ。

この二つの違いを「終了の仕方」と「簡約ステップ数」について見ていこう


Termination

inf = 1+inf

評価戦略に関わらず、この式は終わらない。

fst (1,inf)

この式は、call-by-valueだと終わらない。call-by-nameだと終わる。


More generally,we have the following important property:if there exists any evaluation sequence that terminates for a given expression,then call-by-name evaluation will also terminate for this expression,and produce the same final result.

In summary,call-by-name evaluation is preferable to call-by-value for the purpose of ensuring that evaluation terminates as often as possible.

call-by-nameで終わる時はcall-by-valueでも終わる。call-by-valueでしか終わらない場合がある。つまり、その点ではcall-by-valueの方が優れてるって事。

結論:遅延評価なら、理論的に終了できるなら終了する。

Number of reductions

call-by-valueは引数が一度しか評価されない。call-by-nameでは引数がコピーされてから評価されることがあるので、同じ引数が何度も評価されうる。簡約のステップ数はcall-by-nameの方が多くなるかもしれない。しかしHaskellは、引数の評価が一度しかされないような戦略を取っている。

コピーされるのは引数そのものではなくて、引数へのポインタであって、引数の評価は一度で済むようになっている(グラフ簡約)。このような評価戦略を遅延評価とかcall-by-needとか言う。

lazy evaluation never requires more steps than call by value evaluation

結論:遅延評価は、call-by-value評価よりもステップが多くなったりはしない。


Infinite structures

遅延評価のおかげで無限の構造を持つデータが扱える。

using lazy evaluation,rxpressions are only evaluated as much as required by the context in which they are used.

「ones = 1:ones」で定義されるようなデータは「無限リスト」というよりは潜在的無限リストであって、コンテクストが必要とされる部分だけが評価されるんだ。

Modular Programming

lazy evaluationのおかげでデータとコントロールを分離できる。

これはモジュラリティという有り難い特性を産んでくれる。

Strict Application

Haskell遅延評価が基本だけど、関数適用正格にもできる。

例えば、(f $! x)では、xをトップレベルの評価をしてからfが適用される。

遅延評価はときおりサンクを積み上げるので、正格適用はスペース節約に便利。

foldlの正格版のfoldl'は便利かもしれない。

however strict application is not a silver bullet that automatically improves the space behavior of Haskell programs.Even for relatively simple examples,the use of strict application is a specialist topic that requires careful consideration of the behavior of lazy evaluation.

(しかしながら正格適用は鋼の弾丸にあらず、自動的にHaskellプログラムのメモリを扱う振舞いを改善したりはしない。比較的単純な例でさえ、正格適用は専門的な議題であり、注意深い配慮を遅延評価の振舞いへと払わなくてはならない。

という事なので必要になるまで手を出さない方が良さそうだ。

2008-12-24

memo memo - 他人のHaskell日記 を含むブックマーク

圏論でのモナドを構成する三人組と、それに対するApplicative,Functorでの対応

関手(map) 単位(unit) 乗法(flatten)
(a -> b) -> (m a -> m b)a -> ma m (m a) -> m a
Functor fmap
Applicative <$> pure
Monad liftM return join

Functor Laws

 fmap id  ==  id
 fmap (f . g)  ==  fmap f . fmap g

Applicative Laws

--identity
    pure id <*> v = v 
--composition
    pure (.) <*> u <*> v <*> w = u <*> (v <*> w) 
--homomorphism
    pure f <*> pure x = pure (f x) 
--interchange
    u <*> pure y = pure ($ y) <*> u 

Monad Laws

 return a >>= k  ==  k a
 m >>= return  ==  m
 m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h

Haskell generally uses a pair of functions called return and bind (>>=), but it is more natural sometimes to begin with map (fmap), return and join

(訳:Haskellは一般にreturnやbindと呼ばれる関数のペアを使うが、map(fmap),return,joinからはじめたほうがより自然である。)

no title

Monad は操作の積の抽象化ともいえる. 任意の a, b について、自然な (>>=) :: m a -> (a -> m b) -> m b が与えられるということは、任意の a について自然な m m a -> m a が与えられることと同値になる.これが積の正体. 2重配列を1重に直す Array#flatten は、この手の積の一例.

no title


モナドの勘所 モナドの勘所 - 他人のHaskell日記 を含むブックマーク

http://d.hatena.ne.jp/m-hiyama/20060421/1145613700

http://d.hatena.ne.jp/m-hiyama/20060418/1145322223

ここを見ながらお勉強。少しだけモナドに親しくなれた気がする。

概要

  • どんな関数も「(1段の)モナド」を返す関数に変換できるよ。
  • モナドを返す関数」同士を合成して「モナドを返す関数」にできるよ。
  • その合成には結合律が成り立つよ。
  • 結合律はモジュール性を産むよ。

いきなり脱線:無視しましょう。

普通、圏論で使われるのは、Haskellで言うところの(=<<)であるが、Haskellは(>>=)の方を優遇している。これはflip(=<<)なので意味的には同じで引数の順番が違う。(>>=)が優遇されているのは、流れを左から右にしたいからであろう。そうすれば、シンタックスシュガーであるdo記法に書き換えたときに、処理を上から下に書ける命令的スタイルと相性がいいのである。

普通の関数適用で考えてみよう。f (g (a))という関数があったとき、流れは右から左になっている。

次の関数で考える。

hoge = blur $
       fizz $
       buzz $
       bar  $
       a

このときabar適用され,それにbuzz適用され,それにfizzが適用され,blur適用され、という風に流れが下から上になっている。($$)をflip($)で定義し、結合性優先順位を適切に設定すれば、

hoge = a    $$
       bar  $$
       buzz $$
       fizz $$
       blur

と書ける。こうすれば流れは上から下になってくれる。そういう事がしたいのでHaskellでは(=<<)より(>>=)を選んだと思われる。

さて、そういうHaskellの都合は無視しよう。

今ここにHaskellにおける関数(=<<),returnが存在するとする。

これらは圏論の用語ではそれぞれextとunitと呼ばれる。この二つと型構成子mを合わせてKleisli tripleと呼び、モナドの定義に使われる。

具体的には、次のモナド則を満たさなければならない。

   1.  (return x) >>= f == f x
   2. m >>= return == m
   3. (m >>= f) >>= g == m >>= (\x -> f x >>= g)

圏論のモナドの定義には代数スタイルという別の手段があり、それは(unit,fmap,join,型構成子)を元に作られる。(下の方で、Kleisli tripleからfmap,joinを作れる事を議論する。)


モナドと関係の無い普通の関数「f::A -> B」を仮定する。

この関数に「unit::a -> m a」を後合成することで「unit.f::A-> m B」の形の関数を得られる。

型が「(ほにゃらら)-> m (なんたら)」な関数をM resulticな関数と言う。要するにモナドを返す関数である。

普通の関数はunitと後合成することでM resulticな関数に出来る

  • 圏論の議論より、M resulticな関数「(ほにゃらら)->m a」はext(=<<)を適用することで、お尻を持ちあげられる。つまり「m(ほにゃらら)→ m a」となる。

与えられたm a型の値をそのまま返す関数id::m a -> m a は「(ほにゃらら)-> m a」の形になっているのでM resulticである。M resulticな関数のお尻は持ちあげられるのでやってみると「id^:m(m a) -> m a」という形になる。

こいつがjoinの正体であり、Listのconcatの正体でもある。この関数を使えば多重なmを一段減らせる。

mが多段になっていたとしても(Maybe Maybe Maybe Maybe Maybe Maybe Int)、何度も適用すれば最後には1段まで落とせる(Maybe Int)

extからjoinが作れる。そしてjoinを使えばどんなモナドも1段まで落とせる。

  • 圏論の議論より、M resulticで無い関数「(ほにゃらら)→a」にextは適用できない(お尻だけ持ちあげることはできない。)

ということは、extを使って「(ほにゃらら)->X」から「m(ほにゃらら)->X」を作ることができないという事を意味する。(unitを使っても無理である。)今作るのに断念した「m(ほにゃらら)->X」は、モナドを外す関数である。つまりモナドの外側をはいで1段のモナドまで落とすことはできても、最後のmを脱がせるようなモナド的な操作は存在しない。 「モナドには出口が無い」というのは、そういう意味である。

2つのM resulticな関数を合成し、1つのM resulticな関数を作れる.f:X→m Y とg:Y→m Zという関数があったとする。まずgのお尻を持ちあげてg^:m Y-> m Zという関数を作る。fの値域とg^の定義域は同じm Yなので、この二つは合成できる。その結果、g^.f:X->m Z が出きる。この2つのM-resulticな関数の合成をKleisli Compsitionと呼ぶ。

Control.Monadではモナド合成演算(Kleisli composition演算子)は次のように定義されている。

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
m >=> n = \x -> do { y <- m x; n y }

(訳注:これを変形する。

m >=> n = \x ->  m x  >>= \y->n y -- シンタックスシュガー外し 
m >=> n = \x ->  m x  >>= n       -- ポイントフリー
m >=> n = \x ->  n    =<< m x     -- (=<<)はflip(>>=)
m >=> n = \x ->  (=<<) n  (m x)   -- 演算子は関数
m >=> n = \x ->  (ext n)  (m x)   -- 議論にあわせてextに書き換え。nを部分適用
m >=> n = \x ->  (ext n.m) x      -- 関数合成
m >=> n = ext n.m                 -- ポイントフリー

上で議論した形になった。

ところで、

a >>= b >>= c >>= d >>= e >>= f

a >>= (b >=> c >=> d >=> e >=> f)

と意味的に一緒である。>>=がやってるのは「b,c,d,e,fを合成した関数」のお尻を持ちあげて(拡張)aという座薬を入れる(適用)しているイメージである。

)

この演算子を使えば、Monad則は次のように書き換えられる。

  • "Left identity": return >=> g = g
  • "Right identity": f >=> return = f
  • "Associativity": (f >=> g) >=> h = f >=> (g >=> h)

いまや、モナド合成が結合律を満たすのは明らかだ

no title

手動の型推論により、return:a -> m a、f:a -> m b、g:b -> m a、h:a -> m c のはずなので

  • a-> m a >=> a -> m b = a -> m b
  • b -> m a >=> a -> m a = b -> m a
  • (a -> m b >==> b -> m a ) >=> a -> m c = a->m b >=> ( b -> m a >=> a-> m c )

と書き換えることが出来る。

return を one,>=>を *** と書き換えると

one ***   g = g
f   *** one = f
(f *** g) *** h = f *** (g *** h)

掛算のように見えてくる。これを M-resulticな関数は、Kleisli compositionについて、unitを単位元とするモノイドになっていると表現する。(訂正:型が合致しなければならない制約があるために、正確にはモノイドでは無いそうです)


ところで、

X->YからX->m Y
-あるM-resulticでない関数にunitを後合成すると、頭を持ち上げたM resulticな関数が作れる
X->m Yからm X->m Y
M resulticな関数にextを適用すると、お尻を持ちあげた関数が作れる

この二つの操作を組み合わせればX->Yの全身を持ちあげたm X->m Yが作れる。こいつをmapと呼ぶ。これはHaskellではFunctorクラスのfmapであり(ちなみに、Haskellのリスト関数であるmapをfmapに全て置換しても、そのまま動く。)、MonadクラスのliftMである。

ここでついにjoinに続いてfmapを作ることができた。つまり(unit,fmap,join,型構成子)が揃ったことになる。つまりKleisli tripleで代数スタイルは表現できるということだ。

  • (a-> ____ b) (___にはmが0個以上並んでいるとする)という関数は、なんでも a-> m bという形に出来る
    • a-> b を a-> m bに出来る。(unitを後合成)
    • a-> m......m b (たくさんmが並んでいると考えて。)を a ->m bに出来る。(joinとextを使う)
  • ( )-> m ( )という形の関数同士をKleisli Compositionで合成し、型が( )-> m ( )の形になるようなひとつの関数にできる
結合律とモジュール性

上で、「モナドを返す関数はKleisli Compositionについてモノイド」になっているという事、かけ算にそこが似ている事を述べた。逆に言えば、かけ算について考えることはモナドについて考えることにもなる。

かけ算は結合律をみたす

A * C * C * D * C * C * F * C * C 

これは

A * (C * C) * D * (C * C) * F * (C * C)

と同じである。こう書けるのは結合律のおかげである。

割り算は結合律が働かない。

120 /  2/ 3 / 4 =  5
120 /( 2/ 3)/ 4 = 45

C*Cを3回計算しているので、ここを「モジュール」としてくくりだそう。

C2 = C*Cとする。そうすれば次のように書ける。

A * C2 * D * C2 * F * C2

これが結合律の力である。

さらにモジュールに出来る。

C2 = C*C
(A * C2) * (D * C2) * (F * C2)

f(n) = n * C2 と定義する。

C2=C*C
f(n) = n * C2
f(A)* f(D) * f(F)

これと全く同じ事がモナドにも言える。

モナド則の結合律のおかげで、モナドを返す関数(M-resultic)の組み合わせ(Kleisli Composition)は、その一部を取り出して自由にモジュールにできる。そして、(まだ僕には)なぜかは良くわからないが、いろんな処理がモナドで表現できるのである。それはつまり、いろんな処理を自由にモジュールに分割できる、という事である。

main = do uguu
          gao
          poka
          anpan
          gao
          poka

は次と同じで

air = do gao 
         poka

main =  do uguu
           air
           anpan
           air

これは次と同じである。


crossworld kuchiguse = do kuchiguse
                          air

air = do gao 
         poka

main =  do crossworld uguu
           crossworld anpan

こんな事が出来るのは結合律のおかげである。


かけ算についての上のモジュール性の議論を、実際にHaskellを使って真似てみよう。

a = 5
d = 10
f = 8
c = 3

ret :: Int->Int 
ret n = n

(>>>=):: Int->(Int->Int)->Int
a >>>= f = f a 

multiply :: Int->(Int -> Int)
multiply a b = (a*b)


data MyInt = MyInt Int deriving (Show,Eq)

--A * C * C * D * C * C * F * C * C 
ans = ret 1      >>>=
      multiply a >>>=
      multiply c >>>= 
      multiply c >>>= 
      multiply d >>>= 
      multiply c >>>= 
      multiply c >>>= 
      multiply f >>>= 
      multiply c >>>= 
      multiply c  


-- A * C2 * D * C2 * F * C2    
c2 = \n-> ret n >>>= multiply c>>>= multiply c

ans2= ret 1      >>>=
      multiply a >>>=
      c2         >>>=
      multiply d >>>= 
      c2         >>>=
      multiply f >>>= 
      c2         


--f(A)* f(D) * f(F)

fun x = \n-> ret n >>>= multiply x >>>= c2

ans3= ret 1       >>>=
      fun a       >>>=
      fun d       >>>=
      fun f       

実行結果

*Main> ans
291600
*Main> ans2
291600
*Main> ans3
291600

恒等モナドを使って書き直す。

import Control.Monad.Identity
a = 5
d = 10
f = 8
c = 3

multiply :: Int-> (Int -> Identity Int)
multiply a b = return (a*b)

--A * C * C * D * C * C * F * C * C 
ans = return   1 >>=
      multiply a  >>=
      multiply c  >>= 
      multiply c  >>= 
      multiply d  >>= 
      multiply c  >>= 
      multiply c  >>= 
      multiply f  >>= 
      multiply c  >>= 
      multiply c  

c2 = multiply c >=> multiply c

-- A * C2 * D * C2 * F * C2    
ans2= return 1   >>=
      multiply a >>=
      c2         >>=
      multiply d >>= 
      c2         >>=
      multiply f >>= 
      c2         

fun x = multiply x >=> c2


--f(A)* f(D) * f(F)
ans3= return 1    >>=
      fun a       >>=
      fun d       >>=
      fun f       

BiancaBianca2012/07/24 08:45Deadly accuarte answer. You've hit the bullseye!

iokinaqiokinaq2012/07/25 02:21eAffTE <a href="http://wnespfgrntru.com/">wnespfgrntru</a>

rzefrqgrzefrqg2012/07/26 00:16awec8R , [url=http://waphzksgulwp.com/]waphzksgulwp[/url], [link=http://xqmarjiugccf.com/]xqmarjiugccf[/link], http://aszaetsvysvt.com/

bcwwspbcwwsp2012/07/26 19:06UvNa0Y <a href="http://iicmmzekucpv.com/">iicmmzekucpv</a>

バイアグラバイアグラ2012/11/29 16:04バイアグラジェネリックのhttp://xn--ed-u41gn1j.net/
バイアグラジェネリックの販売 購入はed通販で。
ED治療薬のバイアグラジェネリックの個人輸入を完全サポート致します。
http://xn--ed-u41gn1j.net/generic100.html
バイアグラジェネリック バイアグラジェネリック通販 バイアグラジェネリック販売

2008-12-23

Haskell プログラミングガイドライン Haskell プログラミングガイドライン - 他人のHaskell日記 を含むブックマーク

http://www.haskell.org/haskellwiki/Programming_guidelinesを読みながらメモ。

  • haddockが読めるように

{- |

Module : <File name or $Header$ to be replaced automatically>

Description : <Short text displayed on contents page>

Copyright : (c) <You> and <Your affiliation>

License : similar to LGPL, see LICENSE.txt

Maintainer : Christian.Maeder@dfki.de

Stability : provisional

Portability : portable

<module description starting at first column>

-}

というフォームから開始せよ

  • 「関数はshort&sweetであるべきで、一つの事しかしちゃならん。1画面か2画面で収めろ。ひとつのことをちゃんとやれ。」
  • ほとんどのhaskellの関数は少ない行数であるべき。巨大なデータ型を扱う(まあ、それ自体を避けるべきだけど)場合のcaseは例外。
  • コードは十分かつ明瞭であれ。読みやすく、予想できる変化に対して保守しやすくせよ。理由がなければ、エキゾチックな言語の使用を無駄づかいするな。
  • インデントの深さは指定されていない。
  • {や;を使うのはやめろ
  • コンパイラーの警告に耳をかたむけろ。型宣言をせよ。シャドウイングや未使用の変数は避けよ。パターンは漏れなくダブリなく書け。ありえないパターンはerror <Module Name> <Function>で埋めておけ。
  • 未使用やコメントアウトされたコードをファイルに残すな。読者が混乱する。
  • case式はたいていdeclaration Styleより望ましい。後者は引数の数や関数名が変わったら面倒だから。
  • expression Style(\ t->~)もお勧めしない。const, flip, curry, uncurry、セクション記法、部分適用を使えばたいてい必要ない。まあでもλを消すためだけに補助関数を使うぐらいならexpressin Styleもいいかもしれない。
  • 末尾の空白は全部消せ
  • タブ文字は空白に置きかえろ。
  • 1行80(できれば75)文字以内に収めるべき
  • 全体のモジュールは400行以内にすべき
  • 名前はcamelCaseで付けろ。qualifiedも使え。
  • 部分関数が前提とする条件が明らかでないのならドキュメントを書け。caseを使うなり、事前のテストをするなりして、前提条件が満さされたときのみに部分関数が呼ばれるようにしろ。特にheadを呼ぶときは気をつけろ。あるいは(もっといい手段として)caseを使うことによってheadの使用を避けろ。caseが嫌ならmaybeを使うことで避けれるぞ。とにかく失敗するパターンについては明白であれ。
  • より複雑な事をしようとしているのなら、型の同義名やタプルより適切なデータ型の方が望ましい。そうすれば、後に型クラスを提供するのも簡単になる。コンパイラーにチェックされないため、長期間一貫した型の同義名を使うのは難しい。
  • クラス束縛はデータ型に対してではなく、そのデータを扱う関数に対して行うこと。
  • リスト内包表記は"Short and Sweet"なときだけ使い、普段はmap filter foldrを使え。後々、リスト以外のデータ構造に変えるときも楽だ。
  • 中置き演算子を使えば括弧を減らせるぞ。$や.は便利だぞ。
  • $の周りには空白を残せ.templete haskellで問題がおきないし。(\t->~)と書かずに(\ t ->~)とかけ。
  • letとwhereの混同とネストを防ぐため、top-levelの補助(auxiliary)関数を使え。ただし、その補助関数をexportするな。exportリストは未使用な関数を発見するのに便利。
  • 同じタスクを何度も書いているのに気がついたら、一般化してコードの複製を防げ。バグがでたら面倒だ。
  • 巨大なレコードを扱うときは、コンストラクターを直接使うべきでない。フィールドの数や順番が変わったとき困る。
  • IO,モナド、純粋なdoを含まない関数を厳密に分割することを試せ。
  • Preludeのintaractは使うな。プログラムが(いつも明白ではない)評価順序に依存しないようにしろ。
  • traceはデバッグのためだけに使え
  • Char. List, Maybe, Monadは階層的モジュール名でimportせよ。
  • SetやMapをimportするときはqualifiedを使え。

モジュールを適切にImportしよう


http://www.haskell.org/haskellwiki/Import_modules_properly

他のモジュールから識別子をimportするときにいろいろやりかたがあるが、見た目ほど便利ではないやりかたもある。


推奨:顕示(explicit) import

import qualified Very.Special.Module as VSM
import Another.Important.Module (printf)

非推奨:匿名(anonymous) import

import Very.Special.Module
import Another.Important.Module hiding (open, close)
スタイル
前者では、プログラムを読んでいるときにprintfやVSM.openを見かけたら、その識別子がどこからやってきているのかわかる。後者では、識別子がVery.Special.ModuleからきたのかAnothoer.Important.Moduleからきたのか、それとも他のモジュールから来ているのか区別できない。grepしても無駄だ。Very.Special.ModuleやAnother.Important.Moduleは他のモジュールを再exportしているかもしれないからだ。
複雑性
後者では、importしているモジュールに新たに識別子が加わったら他のモジュールと名前が衝突するかもしれない。だからimportされているをモジュールをアップデートすれば、あなたのコードを破壊する可能性がある。 Another.Important.Module.openを隠したということは、それを軽視している事を意味するが、モジュールがアップデートするときに、この識別子を取り除いたとき、あなたのimportは失敗する。この識別子はまったく必要ではないのに、貴方を何度も困らせることになる。前者では、このような問題は起きない。
正確性
わたしはかつて、匿名importから顕示importに変更したときに、StorableVectorパッケージのバグを見つけたことがある。このモジュールの関数はnot "byte"ではなくunit "element"を計算しなければならないにも関わらず、Foreign.Ptr.plusPtrがimportされていると分った。これは advancePtrが代わりに使われたに違いない。実際にreverse関数はplusPtrを使っており、これは間違っていた。ある無作法は、1バイトよりも大きなsub-vectorsとelementsを扱ったときにのみ観察される。テストスイートはそれを見逃していた。

:例外:Preludeはこれからも不変であることを意図していいるから、Preludeをimportするときにhidingを使うのは問題ない。しかも何も書かなけりゃ自動的にimportされてしまうわけだから。


単純から複雑に

http://www.haskell.org/haskellwiki/Simple_to_complex

一般に、単純な関数を組みあわせて複雑な関数を作るのはいいアイデアだ。

複雑な関数のスペシャルケースとして単純な関数を作るような戦略は、それ単体では上手くいかない。そのような複雑な関数はそもそも、その関数自身に、いろいろなものを組みあわせていかねれば作れない。これでは関数依存のグラフがもつれてしまい、関数は明確な階層にはならない。

Haskell遅延評価機能があるので、単純な関数から複雑な関数を作るのだという原則を忘れてしまいがちだ。数学者がHaskellを遣う11の理由を見てほしい。そこでは「3次元ベクトルの外積を結果の単一成分が必要とされたなら、二つの3次元ベクトルの外積の計算を自動的に簡潔にしてくれる」という遅延評価の利点が紹介されている。

しかしながら、外積の単一成分を計算することは、2x2行列の行列式を計算するのと同じ意味であり、それ自身が有用なものである。

だから、よりよいコンセプトは、外積を行列に簡約する遅延を使わないで、2x2行列の行列式を計算する明確な関数を記述し、外積の計算の中ではこれを3度呼び出すというものである。


他の悪例は数字の線形代数パッケージMatLabだ。その型階層構造は複素数行列から始まっており、ユーザはそこから更に複雑な型を作れる。つまり、より単純な型は存在せず、実数行列もなければ、複素数、実数、整数、ブールも無い。それらは、複素数行列で表現されなければならない。これはあまり自然ではない。なぜなら、transcendent powers のようないくつかの操作は、行列には簡単には移植できないからだ。たくさんの操作において、入力値が例えば1x1行列として適切な特性を持っているかどうかを実行時にチェックされなければならない。実際に、整数や論理値といった、いくつかの種類が後に追加されたが、それはMatLabの基本となる型と奇妙な相互作用をする。

MatLabの言語デザイナーがやった誤りは次のようなものである。

彼等はMatLabが永遠に線形代数用の特別な目的を持った言語でありつづけると考え、この領域で出会うもっとも複雑な型のみを実装することに決めた。MatLabが成長するにつれて、プログラムを新たな需要(画像のインポート、エクスポート、GUIプログラミング等)に合わせるためにプログラムを拡張したが、ユニバーサルな型についての最初の決定は上手くスケールしなかった。

これをHaskellで真似るのは名案ではない。単純な型を作ることから始め、それを使って複雑なものを作ろう。ライブラリの中核に手の込んだ型を使ってはいけない。もしそうだとしても、それらを可能な限り階層の下に(葉のモジュールに)追いやろう。

型クラスメソッド

Data.Listにある多くのメソッド(map, filter, foldr, (++)等)は、他のデータ構造にも一般化できそうです。なぜ一般化されたもの(すなわち型クラスのメソッド)に置き変えられていないのか?と不思議に思います。

  • まず、説明的な理由がある。 foldrのような、高階関数は初心者には理解しがたいものだ。だから彼等は再起を書くことに執着しがちになる。

Data.List.foldlが隠され、その一般化であるData.Foldable.foldl(要素の型や働く関数が一般化されただけにとどまらず、ユーザが利用するような特定のデータ構造も存在しない)のみが使えるようになったとしよう。そこには、もはや具体的なものは存在しない。あるのは抽象だけである。抽象的な関数を一番良く説明するのは例を使ううことだ。例えば sum xs = List.foldl (+) 0 xs……いまや、List.foldlが必要となってしまった。

  • それと近い理由として、コードの分かり易さの理由がある。mapやfilterを見ればリストが使われていると分かる。プログラムの読者が人間型推論を始める必要はない。関数がJustやリストやライトなどをモナドに応じて返しうるとして、それを使ってIO(Maybe a)を返すようなコードを書いたとしたら、プログラムの読者は、帰り値がIOなのかMaybeなのか推論しなければならず、特定の型を処理したいのならば、それを読者に伝えなければならない。
  • 最後でかつ重大な理由に、型安全とコンパイラとのコミュニケーションがある。もしコンパイラが、特定の型のためにプログラミングしているとコンパイラが知っていれば、より強力に型を検査できるし、より正確なエラーメッセージが出せる

Data.List.filterを一般化してみよう。

filter :: (MonadPlus m) => (a -> Bool) -> m a -> m a
filter p m = m >>= \a -> if p a then return a else mzero

これは良いものだが、Data.List.filterを取り替えるべきでない。 一般すぎるコードを書くとコンパイラの型推論は失敗する、GHCiで上の定義のfilterを持っているときに

Prelude> filter Char.isUpper (return 'a')

と書いたとする。これは、どんなモナドだと特定されるべきだろうか?Maybe?IO?リスト?デフォルトの型に頼るべきか?結局、型注釈を付けるはめになるだろう。

これらは、標準ライブラリのみに起こる問題ではなく、カスタム型クラスを設計するときも考慮に入れられなければならない。Slim instance declarations.を見よ

Haskell programming tips Haskell programming tips - 他人のHaskell日記 を含むブックマーク

http://www.haskell.org/haskellwiki/Haskell_programming_tips

序文

このページではコードが改善されていく例を見ていきます。そこから一般的な法則を会得しようと努めましょう。とはいっても何にでも適用できるわけじゃありませんし、趣味の問題もあるかもしれません。私達はそれを知っていますから「これは議論の対象だ」と全ての項目に付け加えないでください。(代わりに/Discussionになら「これは議論の対象だ」と加えても構いません。そこで一定のコンセンサスが得られたなら、このページの中身が変わります。)


簡潔であれ。

車輪を再発明しないように。

標準ライブラリは、役に立つ機能でいっぱいで、時には多すぎるぐらいです。あなたが既存の機能を書き直すなら、読者は、それと標準の関数との違いは何なのかと惑います。しかし、あなたが標準関数を使用するなら、読者は新しくて、何か役に立つものを学ぶかもしれません。 適切なリストの関数を見つけるのにお困りでしたら、このガイドを用いてください。

http://www.cs.chalmers.se/Cs/Grundutb/Kurser/d1pt/d1pta/ListDoc/


明示的な再帰を避けます。

明示的な再帰は一般には悪くはありませんが、高階関数を使った、より宣言的な実装を見つけるべきでしょう。

次のような定義はやめましょう。

raise :: Num a => a -> [a] -> [a]
raise _ [] = []
raise x (y:ys) = x+y : raise x ys

「どれだけのリストが処理されて、出力されるリストの要素がどの値に依存しているのか」を読者が把握するのが難しいからです。ただ、次のように書きましょう。

raise x ys = map (x+) ys

更に次のように書きましょう。

raise x = map (x+)

こうすれば、「全リストが処理されて、出力される要素の各々が、入力されたリストの対応する要素だけに依存する」ことが読者に解ります。

適切な関数を標準ライブラリで見つけることができなかったなら、一般的な関数を抽出しましょう。そうすれば、あなたや他の人がプログラムを理解しやすくなります。Haskellはコードの部分を取り出すのが、とても得意です。それがとても一般的だと分かったなら、それをモジュールに分類して再利用しましょう。それはいずれ標準ライブラリに登場するか、あるいは……既に存在することにあなたがいつか気が付くでしょう。

(訳注:この辺りはd:id:y_fukayaさんが訳してくれました。コメント欄をご覧ください。感謝します!)

このような方法で問題を分解すると、デバッグが楽になるという利点もあります。raise の後者の実装が意図したとおりに動かないときは、map の実装(これは合っていると思いたいですが :-) )を調べることと、(+) に渡されたインスタンスを調べることを、別々に行えます。

もっと一般化できるでしょうか。 raise を map と (+) に分解するこの例は、物事を分割する際の原則の、一つの具体例 (a special case) であるように思えます。原則とは、つまり次の二つに分割することです: コレクション全体に対する繰り返しの処理 (ここでは map) と、コレクションの各要素に適用する処理 (ここでは (x+)) 。あるデータ構造 (リスト、ツリー、その他なんでも) の全体に対する繰り返し処理は、一度書いてしまえば、何度もコピーする必要はありません (少なくとも Haskell では) 。これによりあなたのコードは OnceAndOnlyOnce の原則に従っていられます。この原則は、ある程度以上関数型プログラミングをサポートする言語でないと守るのがとても困難なものです (i.e. Java ではコピー&ペースト式のプログラミングが必要になりますし、C# の delegate の構文は機能しますが不恰好です――ほとんど 金メッキでしょう)





別の例:関数countは特定の特性を満たす要素(つまり述語pがTrueな要素)の数を数えます。

私は次のようなHaskellのコードを発見しました。(より特定の関数に巻きこまれていましたが)

count :: (a -> Bool) -> [a] -> Int
count _ [] = 0
count p (x:xs)
  | p x = 1 + count p xs
  | otherwise = count p xs

このような書き方は、次の書き方に気がついていたら好きにはなれないでしょう。

count p = length . filter p

必要な識別子だけを導入しましょう。

このアドバイスは全ての言語について有効であり、科学的な処理においても重要です(http://www.cs.utexas.edu/users/EWD/transcriptions/EWD09xx/EWD993.html)。 必要な識別子だけを導入してください。GHCに-Wallのようなオプションを渡せば、コンパイラがチェックしてくれることでしょう。

[a | i <- [1..m]]

aは恐ろしく複雑な式であり、iに全く依存していないのだ、という事を理解するのは大変です。

replicate m a

の方が明らかにいいですよね。


ゼロを忘れないで。

ゼロが自然数であったのを忘れないでください。 (訳注:ゼロが自然数かどうかは流儀によるようです)再帰のアンカー(訳注:再帰の終端。これが無いと無限ループに)が適切に選ばれていないと、再帰的関数は非常に複雑になります。「Haskell:離散数学のための良いツール」という本で紹介されている関数tuplesはガードを避けるための良い例です。(訳注:ここでのタプルは、タプル型とは別。一般に「順序を持った組」の事をタプルと言う。)



tuples :: Int -> [a] -> [[a]]
tuples r l
  | r == 1 = [[el] | el <- l]
  | length l == r = [l]
  | otherwise = (map ([head l] ++) (tuples (r-1) (tail l)))
  ++ tuples r (tail l)

何をしているか分かりますか?

(訳注:実行例を載せときます

*Main> tuples 3 [1,2,3,4]
[[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
*Main> tuples 2 [1,2,3,4]
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

)


ガードを取り除いて、リスト内包表記を忘れましょう。

tuples :: Int -> [a] -> [[a]]
tuples 1 l = map (:[]) l
tuples r l =
  if r == length l
    then [l]
    else
      let t = tail l
      in map (head l :) (tuples (r-1) t)
      ++ tuples r t

tuplesの引数にゼロが与えられたら、どんな振舞いをするでしょうか?そのパターンを加えてみましょう。

tuples 0 _ = [[]]

もはやtuples 1のパターンは必要ないので消せます。

tuples :: Int -> [a] -> [[a]]
tuples 0 _ = [[]]
tuples r l =
  if r == length l
  then [l]
  else
    let t = tail l
    in map (head l :) (tuples (r-1) t)
                   ++ tuples r t

r > length lの場合はどうでしょうか。headが失敗するのを放置しておく理由は明らかにありません。与えられたリストよりも長いタプルは作れません。だから空リストを返すことにしましょう。これは特別な状況で私達を救ってくれます。

tuples :: Int -> [a] -> [[a]]
tuples 0 _ = [[]]
tuples r l =
  if r > length l
  then []
  else
    let t = tail l
    in map (head l :) (tuples (r-1) t)
                   ++ tuples r t

「length is evil」だと学びました。次のようにしてみたらどうでしょう。

tuples :: Int -> [a] -> [[a]]
tuples 0 _ = [[]]
tuples _ [] = []
tuples r (x:xs) =
  map (x :) (tuples (r-1) xs)
  ++ tuples r xs

lの長さを何度も何度も計算する必要はなくなりました。コードは読みやすくなり、全ての特別な場合をカバーするようになりました。tuples (-1) [1,2,3]のような場合も含めてです!

このlengthチェックの消去はある状況(例:tuples 24 [1..25])において性能を劇的に悪化させます。length l < rの代わりにnull (drop (r-1) l)を使うこともできます。これは無限リストにも対応しています。下の例を見ましょう。

tailsが提供する全てのsuffixのリストを明示的に計算することによって、一方向の再帰を保つこともできます。

do記法を使ってこうも書けます。

tuples :: Int -> [a] -> [[a]]
tuples 0 _ = [[]]
tuples r xs = do
  y:ys <- tails xs
  map (y:) (tuples (r-1) ys)

リストモナドにおける(=<<)はconcatMapですから次のようにも書けます。

前のバージョンではパターンマッチy:ysが最後の空のsuffixを取り除いていましたが、今や手動でやらなければならなくなりました。そこでinitを使います。

tuples :: Int -> [a] -> [[a]]
tuples 0 _ = [[]]
tuples r xs =
  concatMap (\(y:ys) -> map (y:) (tuples (r-1) ys))
            (init (tails xs))

全てのsuffixのリストはiterate tailでも生成できますが、これはPrelude.tail: empty list".というエラーを発生させて終了します。tailsが生成するものは同じものですが適切に終了します。

より一般的には「Base cases and identities」をご覧ください。

λを使いすぎないようにしましょう。

明示的な再帰と同様に明示的なλは、必ずしも悪いものではありませんが、たいていはもっといい解決策が存在するものです。例をあげましょう。

Haskellカリー化が凄く得意なので、次のように書くのは止めましょう。

zipWith (\x y -> f x y)

map (\x -> x + 42)

代わりに、こう書きましょう。

zipWith f

map (+42)

また、このように書く代わりに

-- 文字列のリストを大文字小文字を区別せずにソートする
sortBy (\x y -> compare (map toLower x) (map toLower y))

こう書きましょう。

comparing p x y = compare (p x) (p y)

sortBy (comparing (map toLower))

これなら明確かつ再利用可能です。実はGHC-6.6以降ではcomparingを書く必要がありません。Data.Ordモジュール(http://www.haskell.org/ghc/dist/current/docs/libraries/base/Data-Ord.html)の中に既に存在していますから。


この特別な例についての簡単な注意:変換を複数回評価することを避けることができます。

sortKey :: (Ord b) => (a -> b) -> [a] -> [a]
sortKey f x = map snd (sortBy (comparing fst) (zip (map f x) x))

(訳注:いわゆるシュワルツ変換ですね。http://www.wdic.org/w/TECH/%E3%82%B7%E3%83%A5%E3%83%AF%E3%83%AB%E3%83%84%E5%A4%89%E6%8F%9B)

経験則として、式が長くなりすぎて簡単にポイントフリーに出きそうにない場合は、とにかく名前を付けた方がいいでしょう。しかしながら、たまにはλが適切な場合もあります。例えば、モナディックなコードの中におけるコントロール構造などです。(次の例におけるコントロール構造foreach2は、殆どの言語ではそもそもサポートすらしていないでしょう。)

foreach2 xs ys f = zipWithM_ f xs ys

linify :: [String] -> IO ()
linify lines
        =foreach2 [1..] lines $ \lineNr line -> do
           unless (null line) $
               putStrLn $ shows lineNr $ showString ": " $ show line
Boolは普通の型

論理表現はガードやif分の中に制限された存在ではありません。こういうくどさは避けましょう。

isEven n
 | mod n 2 == 0 = True
 | otherwise = False

これは、次と一緒です。

isEven n = mod n 2 == 0

シンタックスシュガー(構文糖衣)を賢く使いましょう。

シンタックスシュガーを使えば読みやすくなると主張する人々がいます。以下のセクションでは、シンタックスシュガーを減らした方が読みやすくなる例を示します。「特別な記法は純粋な関数による表現よりも、しばしば直感的である」という主張は良く聞きます。 しかしながら「直感的な記法」というものは、いつだって習慣の問題なのです。最初に見たときは馴染みがないような分析的表現に対する直感は発達させることができるものです。そうなると、時折シュガー(砂糖)を減らそうという習慣が身に付きます。

リスト内包表記

リスト内包表記は利用者を命令的な考え方に留めます。変換よりも変数について考えさせるからです。頭を解き放ちポイントフリーの趣を発見しましょう。

[toUpper c | c <- s]

と書く代わりに

map toUpper s

こう書きましょう

次の例を考えてみましょう。

[toUpper c | s <- strings, c <- s]

値がいったい何に依存しているのか理解するのに時間がかります。sやcが何度使われるのか明白ではありません。次と比べましょう

map toUpper (concat strings)

これより明白なものはありませんね。

高階関数を使っていれば、リストから他のデータ構造へと簡単にスイッチできます。

次の二つを比較しましょう

map (1+) list
mapSet (1+) set

Functorクラスの標準インスタンスが存在すれば、次のコードを両方の場合に使えます。

fmap (1+) pool

高階関数について知らなければ、並列リスト内包表記が欲しくなるでしょう。これは不幸な事ですが今やGHCではサポートされています。これは間違いなく過剰です。既に、たくさんのzip風のそれが、素晴しい仕事をしているからです。


do 記法

do記法は命令的な性質(隠れた状態や、実行の順番など)のコード片を表現するのに便利です。しかしながら、do記法が関数を使って表現できる事を覚えていると役に立つことがあります。

do
  text <- readFile "foo"
  writeFile "bar" text

と書く代わりに、こう書けます。

readFile "foo" >>= writeFile "bar"
do
  text <- readFile "foo"
  return text

というコードは「Monadが満たさねばならない法則」のおかげで次のように書けます。

readFile "foo"

あなたは、次のコードが

do
  text <- readFile "foobar"
  return (lines text)

次のコードより複雑であることにも同意するでしょう。

liftM lines (readFile "foobar")

ところでFunctorクラスのfmapメソッドとMonad-basedな関数であるliftMは同じものです。(両方とも正しく定義されていればですが。)

(訳注:Monadは論理的にFunctorなのにも関わらず、FunctorがMonadのスーパークラスではないことを問題視し、クラス階層を変更すべきだという提案がなされています。 http://www.haskell.org/haskellwiki/Functor_hierarchy_proposal をご覧ください。)


「より複雑だ」ということは「悪い」という事を意味しません。これよりもdo記法が長くなるようならば、do記法とfmapを混ぜるべきではないでしょう。「自然であれ」という事を考えるようにしましょう。変化させるのは、変化から何か得られるときのみにしましょう。


ガード

免責:このセクションはガードを使うなとアドバイスしているのではありません。両方とも使用可能ならばパターンマッチングの方がガードよりも好ましいと言っているのです。

-- ダメな実装
fac :: Integer -> Integer
fac n | n == 0 = 1
      | n /= 0 = n * fac (n-1)

これは階乗を求める関数です。この例のようにガードをたくさん使うと、問題がたくさん発生します。

最初の問題は、ガード条件が恣意的に複雑なため(GHCは-Wallオプションを使っていれば警告します。)コンパイラがこのようなガードに漏れがないのかチェックするのはほとんど不可能だと言うことです。

その問題と、漏れのあるパターンがもたらす潜在的なバグを回避するためにotherwiseガードを使えます。

-- いくらか改善された実装
fac :: Integer -> Integer
fac n | n == 0    = 1
      | otherwise = n * fac (n-1)

これが好ましいのには別の理由もあります。人間にとって可読性が高いという点と、コンパイラが最適化しやすいという点です。

このような単純なケースではあまり問題になりませんが、もっと複雑な場合でも、otherwiseを見れば、それが使われるのは他のガードが失敗したときだけだと、ただちに明らかになります。 同じ事がコンパイラにも言えます。Trueの同義語であるotherwiseを最適化するのは可能ですが、他の殆どの式に対しては期待できません。

-- シュガー減量  (if-then-elseの多弁さは、シュガーと捉えることができますが……)
fac :: Integer -> Integer
fac n = if n == 0
          then 1
          else n * fac (n-1)

ifには別の問題群があります。レイアウトルールに関するものであったり、ifのネストは読みにくいというものだったりします。(ifのネストを避けたいならCaseの項目を読んでください)

この特別な例では、パターンマッチングを使うことで、より簡単にできます。

-- 良い実装
fac :: Integer -> Integer
fac 0 = 1
fac n = n * fac (n-1)

実際には、このケースでは、もっと読みやすいバージョンがあります。それは明示的な再帰を使わない方法です。

-- 素晴らしい実装
fac :: Integer -> Integer
fac n = product [1..n]

これは効率的でもあります。productはライブラリの作者によって最適化されていると思われます。GHCで最適化を有効にして実行すると、このバージョンが使用するスタックスペースはO(1)になります。それと比べて、前のバージョンはO(n)のスタックスペースを使用します。

このバージョンと前のバージョンには違いがあります。負の数が与えられると前のバージョンは終わりませんが、このバージョンは1を返します。

いつだってガードがコードを綺麗にするとは限りません。次のコードを比較してみましょう。

foo xs | not (null xs) = bar (head xs)
foo (x:_) = bar x

あるいは次の二つの例を比較しましょう。(パターンガードの例)

parseCmd ln
   | Left err <- parse cmd "Commands" ln
     = BadCmd $ unwords $ lines $ show err
   | Right x <- parse cmd "Commands" ln
     = x
parseCmd ln = case parse cmd "Commands" ln of
   Left err -> BadCmd $ unwords $ lines $ show err
   Right x  -> x

読者がeither関数と親しみ深ければ、次のコードもいいでしょう。

parseCmd :: -- add an explicit type signature, as this is now a pattern binding
parseCmd = either (BadCmd . unwords . lines . show) id . parse cmd "Commands"

ついでに言えば、コンパイラーはしばしば数値パターンについての問題を抱えています。たとえばパターン0は、実際は「fromInteger 0」です。関数のパラメータのパターンとして一般的できない計算が呼ばれることを意味します。これを説明するために、次の例について考えてみましょう。

(訳注:Haskell98の次の標準規格であるHaskell Primeの議論において、数値パターンを排除すべきでないか、という提案がなされています。「みんな使っているし、無いと冗長になる」という反論もなされています→http://hackage.haskell.org/trac/haskell-prime/wiki/RemovalCandidates )


data Foo = Foo deriving (Eq, Show)
 
instance Num Foo where
    fromInteger = error "forget it"
 
f       :: Foo -> Bool
f 42    = True
f _     = False

 *Main> f 42
 *** Exception: forget it

ガードは必要なときのみ使いましょう。可能なときはいつでもパターンマッチングに頼りましょう。

n+k パターン

数値型へのパターンマッチを認めるためにHaskell 98はいわゆる n+k patternsを提供しています。

take :: Int -> [a] -> [a]
take (n+1) (x:xs) = x: take n xs
take _     _      = []

しかしながら、これらは計算の複雑性を隠し、曖昧さを導入していると、しばしば批判されています。詳しくは/Discussionを読んでください。

このパターンは、より一般的なViews提案(残念ながら、長い時間がたっているのにもかかわらず実装されたことはありませんが)に包括されます。

(訳注:n+kパターンはHaskell Primeでは排除される候補のひとつです。http://hackage.haskell.org/trac/haskell-prime/wiki/RemovalCandidates)

効率と無限

経験則「無限のデータ構造に対しても意味が通るはずの関数が、無限のデータに対して適用することで失敗するようであれば、その関数は恐らく有限のデータに対しても適用したときも効率が悪い。」

必要も無いのにリストの長さを尋ねるのは止めましょう。

リストxが空かを調べるのに次のように書くのは止めましょう。

length x == 0

このように書くとHaskellはリスト全てのノードを作成せねばならず、無限リストの場合Falseが帰ってくるべきなのに失敗します。(ちなみに、それでもリストの中身までは評価されないでしょう。)

これと比べ

x == []

は高速です。しかし、リストxの型を[a]だとすると、aはEqクラスの型でなければなりません。

ベストな手段は次の通りです。

null x

加えていうと、length関数を使用しているとき、多くの場合は、問題を十分に限定できていないのです。リストの長さが知りたいのではなく、「リストが少くともある長さであるか」をチェックしたいのなら、lengthを「リストの長さが必要な最小限の長さよりも長いか」を調べるatLeast関数で置き変えるべきです。

atLeast :: Int -> [a] -> Bool
atLeast 0 _      = True
atLeast _ []     = False
atLeast n (_:ys) = atLeast (n-1) ys

再帰を避けるために、効率が落ちてもいいのなら(takeもlengthもノードの数を数えなければならないので無駄があります)次のように書けます。

atLeast :: Int -> [a] -> Bool
atLeast n x = n == length (take n x)

再帰を避けながら、効率も落としたくないのならば、次のように書くといいでしょう。

atLeast :: Int -> [a] -> Bool
atLeast n =
  if n>0
    then not . null . drop (n-1)
    else const True

あるいは次のような書き方もできます。

atLeast :: Int -> [a] -> Bool
atLeast 0 = const True
atLeast n = not . null . drop (n-1)

「あるリストを、他のリストの長さと同じだけ短かくしたい」場合に、次のコードでは同じ問題が起きます。

take (length x) y

これは、巨大なリストに対しては非効率であり、無限リストに対しては失敗します。しかしながら、無限リストから有限のprefixを抽出することは便利かもしれません。それなら代わりにこう書きましょう。

zipWith const y x

これなら上手く動きます。

lengthはgenelicLengthに置きかえることができ、それはPeano数が利用できることを覚えておきましょう。(訳注:genelicLengthとPeano数(遅延に的した構造を持つ)の組み合わせで遅延カウントができるらしいです→http://www.haskell.org/haskellwiki/Peano_numbers)


必要もないのに最小値を尋ねるのは止めましょう。

次の関数isLowerLimitは、ある数がリストの最小値かどうかをチェックします。


isLowerLimit :: Ord a => a -> [a] -> Bool
isLowerLimit x ys = x <= minimum ys

これはysが無限ならば明らかに失敗します。これは問題だといえるでしょうか?

次と比較しましょう.

isLowerLimit x = all (x<=)

この定義ならxが最小値でなければ無限リストを与えても終了します。

xより小さな値が見付かればただちに終了します。つまり有限のリストに対しても速いということです。更に空リストに対しても動きます。


共有しましょう。

もし、中身は同じで長さが伸びていくリストを作りたいのなら、次のようには書かないでください。

map (flip replicate x) [0..]

なぜなら、2乗の空間と実行時間を必要とするからです

iterate (x:) []

こうすれば、リストはsuffixを共有するので、作成するのに線形の空間と実行時間しか必要としません。


(訳注:「tailsもリストのコピーを作らなくて効率的」という話も知っておくと便利なケースがあるかもしれません。 http://haskell.g.hatena.ne.jp/jmk/20060913)


適切なfoldを選びましょう。

どちらのfoldが状況に適切なのかというアドバイスが「

[x !! i - i | i <- [0..n]]
zipWith (-) x [0..n]
効率性ヒントの項目で述べられている通りです。


型クラス制約を減らそう

delete, (\\), nub等の関数を使うにはEQクラスの型が必要になることを覚えておきましょう。リストに複数の等しい要素が含まれていたら期待したようには動いてくれないという事と、関数のように要素の型が比較できないなら使えないという事です。

例)次の関数は入力されたリストxsから、xsの要素を一つづつ取り除いた、それぞれのものを返します。

何をいってるか解りますか?分かりませんか?恐らくコードならもっと理解しやすいと思います。

removeEach :: (Eq a) => [a] -> [[a]]
removeEach xs = map (flip List.delete xs) xs

これは次のように書き換えるべきです。

removeEach :: [a] -> [[a]]
removeEach xs =
   zipWith (++) (List.inits xs) (tail (List.tails xs))

これならaが関数型であっても、xsに等しい要素があっても完璧に動きます。


  • 整数を考慮していないならIntを使うのを止めましょう。

Cのスタイルのように、あらゆるものに整数を使う前に、もっと特定の型を使う事を考えましょう。

0と1にしか興味がないのならBoolを使いましょう。事前に定義された選択肢があり、数値的な演算子が必要ないのなら、列挙型を試しましょう。

次のように書かず

type Weekday = Int

このように書きましょう。

data Weekday = Monday
             | Tuesday
             | Wednesday
             | Thursday
             | Friday
             | Saturday
             | Sunday
  deriving (Eq, Ord, Enum)

これは ==, <, succのような目的に敵った演算子は許容し、+, *のような意味のない演算子を禁止します。

こうすれば、間違って曜日と数字を混ぜたりできませんし、weekdayをパラメータに取る関数のシグニチャを見れば、期待するデータの種類が何なのかを明白に分かります。

列挙型が不適切な場合は、必要としている型に最も近い型を転用してnewtypeを定義できます。

たとえば、オブジェクトを一意的な識別子と関連付けたいとします。

整数型を選びたくなるかもしれませんが、演算が必要ないのなら、次のように実際の整数と区別した型を作れます。

newtype Identifier = Identifier Int deriving Eq

その他

IOとデータ処理を分離しましょう

IO Monadをどこでも使うのはよくありません。 ほとんどのデータ処理はIOとの相互作用無しに書けます。 データ処理とIOを分けて、(実行順序を特定する必要がなく、何が実際に必要な計算なのか考えなくてすむように)純粋な関数的に処理できるようにしましょう。純粋関数的にデータを処理し、それを簡潔なIOとの相互作用で出力すれば、遅延評価の便益を受けられます。

-- import Control.Monad (replicateM_)
replicateM_ 10 (putStr "foo")

は明らかに次のコードよりも悪いです。

putStr (concat $ replicate 10 "foo"

同様に、

do
  h <- openFile "foo" WriteMode
  replicateM_ 10 (hPutStr h "bar")
  hClose h

は次のように短かくできます。

writeFile "foo" (concat $ replicate 10 "bar")

これは、失敗したときはファイルハンドルhが適切に閉じられるようになっています。

カスタムした分布を持つランダムな値を計算したい関数(distInvは分布関数を反転させます)はIOを使って

次のように定義できます。

randomDist :: (Random a, Num a) => (a -> a) -> IO a
randomDist distInv = liftM distInv (randomRIO (0,1))

しかし、こうする必要はありません。乱数生成器の状態を覚えるためだけに、全ての世界の状態を記憶しておく必要は無いからです。そこで、次のようにしたらどうでしょう。

randomDist :: (RandomGen g, Random a, Num a) => (a -> a) -> State g a
randomDist distInv = liftM distInv (State (randomR (0,1)))

Stateを走らせることで実際の値を得られます。次のようにしましょう。

evalState (randomDist distInv) (mkStdGen an_arbitrary_seed)
quotとremについては忘れましょう。

こやつらは負の被除数を扱うのを難しくさせます。

divとmodは、たいていいつも良い選択であり、b > 0なら次を満たします

a == b * div a b + mod a b
mod a b < b
mod a b >= 0

quotとremでも最初の等式はTrueですが,他の二つはmodにはTrueなのにremにはFalseです。

つまりmod a b は[0..(b-1)]の間にaをラップするものですが、rem a b の符号はaの符号に依存しているのです。

これは「優秀な理由」というよりも経験の問題のようです。なので被除数の符号が除数の符号よりも重要な場合もあると反論するかもしれません。しかしながら、私はそのような適所を見かけたことはありません。quotとremが使われていたとしても、多くの場合はdivやmodを使った方が良いのです。

  • 連続的に数えられたトーンピッチを音程C(ド),D(レ),E(ミ)のクラスに変換したい場合: mod p 12
  • 要素の数がmの倍数になるようにpaddingする場合: xs ++ replicate (mod (- length xs) m) pad
  • 日付データを曜日に換算する場合: mod n 7
  • パックマンが画面から走り出て画面の逆から現われる場合: mod x screenWidth

次の文章も読んでください。


fromJustやheadのような部分関数

fromJustやheadのように、特定の値が入力されると失敗するような関数は避けましょう。実行時にしか検知できないエラーを起こします。プログラムを別のやりかたで組み上げることによって部分関数の使用を回避できないか考えるか、もっとエラーを起こさないような限定された型を選びましょう。

次のように書かず

if i == Nothing then deflt else fromJust i

このように書きましょう。

fromMaybe deflt i

(==)はEQクラスインスタンスをiの型として要求しますがfromMaybeはパターンマッチングを行うので、それを必要しない事に気付いてください。


このようにfromJustを避けることができない場合でも、とにかくfromMaybeを使って「あなたの状況では何故値が常にJustになると考えているのか」をerrorに付記してください。

fromMaybe (error "Function bla: The list does always contains the searched value")
          (lookup key dict)

「決して空にならないような型」を使うことでheadの問題を避けることが出来ますが(訳注:CycloneやCwという言語では、そうしているようです。) 、Maybeを使ったやりかたもあります。

関連するリンク

Haskell初心者がやりがちな一般的な間違いや間違った信念 Beginners
いくらか一般的な、それほど一般的でないHugs Errors

y_fukayay_fukaya2008/12/24 18:05はじめまして。
意味がとれないとおっしゃっている部分があったので、翻訳の参考になればと訳してみました。原文を盛大に破壊しています (間に勝手に文を補ったりしてます) が、ニュアンスとしてはこういうことではないかと思います。


Decomposing a problem this way also has the advantage that you can debug more easily. If the last implementation of raise does not show the expected behaviour, you can inspect map (I hope it is correct :-) ) and the invoked instance of (+) separately.

このような方法で問題を分解すると、デバッグが楽になるという利点もあります。raise の後者の実装が意図したとおりに動かないときは、map の実装(これは合っていると思いたいですが :-) )を調べることと、(+) に渡されたインスタンスを調べることを、別々に行えます。

Could this be stated more generally? It seems to me this is a special case of the general principle of separating concerns: iteration over a collection vs operating on elements of a collection should apply. If you can write the loop over a data structure (list, tree, whatever) once and debug it, then you don't need to duplicate that code over and over (at least in haskell), so your code can follow the principle of Wiki:OnceAndOnlyOnce ; Wiki:OnceAndOnlyOnce is a lot harder in languages that don't provide a certain level of functional programming support (i.e. Java requires copy and paste programming, the delegate C# syntax is clumsy but workable - using it is almost Wiki:GoldPlating).

もっと一般化できるでしょうか。raise を map と (+) に分解するこの例は、物事を分割する際の原則の、一つの具体例 (a special case) であるように思えます。原則とは、つまり次の二つに分割することです: コレクション全体に対する繰り返しの処理 (ここでは map) と、コレクションの各要素に適用する処理 (ここでは (x+)) 。あるデータ構造 (リスト、ツリー、その他なんでも) の全体に対する繰り返し処理は、一度書いてしまえば、何度もコピーする必要はありません (少なくとも Haskell では) 。これによりあなたのコードは Wiki:OnceAndOnlyOnce の原則に従っていられます。この原則は、ある程度以上関数型プログラミングをサポートする言語でないと守るのがとても困難なものです (i.e. Java ではコピー&ペースト式のプログラミングが必要になりますし、C# の delegate の構文は機能しますが不恰好です――ほとんど Wiki:GoldPlating でしょう)


文中 Wiki:OnceAndOnlyOnce や Wiki:GoldPlating と書かれているのは、推測ですが Wikipedia への interwiki ではないかと思います。英語版 Wikipedia の OnceAndOnlyOnce の項は "Don't repeat yourself" の項にリダイレクトされていて、これには日本語版の記事 (http://ja.wikipedia.org/wiki/Don't_repeat_yourself) がありますから、これにリンクするのが無難かなあと思います。

GoldPlating は自分もいまいち意味がとれていません。単純に、関数型的な機能は C# という言語に後付けされた(金をメッキ処理された)ものだということかと推測するのですが、何か他の意味合いで使っているのかもしれません。

以上、お役に立てば幸いです。

taninswtaninsw2008/12/24 19:27ご協力ありがとうございます。反映……というかコピペさせていただきました。

http://en.wikipedia.org/wiki/Gold_plating_%28disambiguation%29
ここの項目を見る限りでは「望まれたものを越えた価値の無い無駄な機能追加」という意味なのかなぁ、と思いました。

ThitaThita2013/03/29 16:50At last, smooene who comes to the heart of it all

fgfiinfgfiin2013/03/31 22:16oHw9Ul , [url=http://xxqubjmimmgh.com/]xxqubjmimmgh[/url], [link=http://tsulqdptgaqj.com/]tsulqdptgaqj[/link], http://sdafaupukvii.com/

bnbkfxdfabnbkfxdfa2013/04/01 05:09yRGC6C <a href="http://lyuyazfamgba.com/">lyuyazfamgba</a>

uhyfaumoeguhyfaumoeg2014/04/12 20:58vmiyjibtlfmm, <a href="http://www.ftgcmaoiso.com/">mgofkntkfh</a> , [url=http://www.grpdcysrgi.com/]pylrgcmetb[/url], http://www.uoojtwewed.com/ mgofkntkfh

uqawoporaxofuqawoporaxof2017/03/27 02:57http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

ixubviqomupuixubviqomupu2017/03/27 03:01http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

ilauniwoqibehilauniwoqibeh2017/03/27 03:09http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

utoweliutoweli2017/03/27 03:15http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

idibinarizoidibinarizo2017/03/27 03:20http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

ijizifisuodiijizifisuodi2017/03/27 03:29http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

eqaupimelxoeqaupimelxo2017/03/27 03:34http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

oduyudaixuoduyudaixu2017/03/27 03:53http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

ezetaciezetaci2017/03/27 03:58http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

upuguwatbaupuguwatba2017/03/27 04:11http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

esihibepiqpicesihibepiqpic2017/03/27 04:18http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

azafiqiniuuazafiqiniuu2017/03/27 04:37http://usa-onlineprednisone.net/ - usa-onlineprednisone.net.ankor <a href="http://salbutamol-ventolin-buy.net/">salbutamol-ventolin-buy.net.ankor</a> http://online-viagracanada.net/

oepohehajuuheoepohehajuuhe2017/05/21 06:45http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

oqinavuvezeqioqinavuvezeqi2017/05/21 06:58http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

axoneujitorfaxoneujitorf2017/05/21 07:08http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

aigoxeiyifoaigoxeiyifo2017/05/21 07:10http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

iksewumoiksewumo2017/05/21 07:13http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

ejurijuxuejurijuxu2017/05/21 07:20http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

orjozovocoborjozovocob2017/05/21 07:30http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

ejepukavubozaejepukavuboza2017/05/21 07:33http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

ukezosoxalaukezosoxala2017/05/21 07:57http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

onominwinonominwin2017/05/21 08:11http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

edijuhizeyoedijuhizeyo2017/05/21 08:11http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

equsisqoluequsisqolu2017/05/21 08:23http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

aquxowibephibaquxowibephib2017/05/22 07:19http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

aqelovoaqelovo2017/05/22 07:33http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

okuremusulobiokuremusulobi2017/05/22 17:15http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

eueroyuzieueroyuzi2017/05/23 07:23http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

ufeboziyerogufeboziyerog2017/05/23 19:17http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

apuexibaloapuexibalo2017/05/23 19:30http://100mgcheapest-price-viagra.com/ - 100mgcheapest-price-viagra.com.ankor <a href="http://tadalafil-buy-5mg.com/">tadalafil-buy-5mg.com.ankor</a> http://20mgprednisone-order.com/

ecaduqivaecaduqiva2017/08/11 08:25http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

opuhokazopuhokaz2017/08/11 08:38http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

acuquhoacuquho2017/08/11 08:38http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

galopacirgalopacir2017/08/11 08:46http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

ejohatabakejohatabak2017/08/11 08:51http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

afohqiqunaceafohqiqunace2017/08/11 08:59http://20mg-cheapesttadalafil.com/ - 20mg-cheapesttadalafil.com.ankor <a href="http://20mg-cheapesttadalafil.com/">20mg-cheapesttadalafil.com.ankor</a> http://20mg-cheapesttadalafil.com/

2008-12-21

Monad,Functor,Applicative Monad,Functor,Applicative - 他人のHaskell日記 を含むブックマーク

  • <*> とap
  • pureとreturn
  • fmapと<$>とliftM
  • *> と >>

って全部一緒?

だとすると、

たとえば、

do v1 <- proc1

v2 <- proc2

return (v1, v2)

という処理があるとしよう。これはこう書ける。

pure (,) <*> proc1 <*> proc2

すげースッキリする。さらに、先頭が pure の場合にはまた別の演算子もあり、

(,) <$> proc1 <*> proc2

と書けばよい。ふーむ。いろいろ書けそうだ!

http://haskell.g.hatena.ne.jp/jmk/20061130

liftM2 (,) proc1 proc2でもいいのかな。

Applicativeを使ってみる。  Applicativeを使ってみる。 - 他人のHaskell日記 を含むブックマーク

テキスト正規化トラバース1回バージョン(http://ja.doukaku.org/comment/8234/)をApplicativeとReaderモナドで書き直してみる。

import Control.Monad.Reader
import Control.Applicative
test = "hoge\nPOPOPOPOPO\n\nPINGPONG\n"

padding' :: String->Int->Int->Reader Char (Int,String)
padding' []         i  m = return (0,[])
padding' ('\n':xs)  i  m = do (cMax,cStr) <- padding' xs 0 (max i m) 
                              padStr      <- padding'' ((max cMax m) -i)  
                              return (max m cMax,padStr++cStr)
padding' (x:xs)     i  m =  (\(cMax,cStr)->(max m cMax,x:cStr)) <$> padding' xs (i+1) (max i m) 

padding'' :: Int->Reader Char String
padding'' n = (++ "\n").replicate n  <$> ask

padding text ch = snd $ runReader (padding' text 0 0) ch

はじめて使うjoin  はじめて使うjoin - 他人のHaskell日記 を含むブックマーク

do v1 <- proc1

v2 <- proc2

finalProc v1 v2

な場合にはうまくないな。

http://haskell.g.hatena.ne.jp/jmk/20061130

join使えばいけるようです。

import Control.Monad
import Control.Applicative
import Data.Char

main = join $ printLines <$> getLine <*> getLine

printLines a b = mapM_ (putStrLn.map toUpper) [a,b]

あるいは

main = join $ liftM2 printLines getLine getLine

てな感じで。

haskell-jp [1-150]

IOモナドについて

Q http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/53

A http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/54

テスト手法について

論点の整理 http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/102

QuickCheck+Darcs,undefinedを使う方法

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/128

関連エントリ


GHC 6.8.1

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/138

Flymake-Haskell

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/140

http://madscientist.jp/~ikegami/diary/20071108.html#p01

http://www.emacswiki.org/cgi-bin/emacs/FlymakeHaskell

HaRe

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/106

Haskell用のリファクタリングツール

http://www.cs.kent.ac.uk/projects/refactor-fp/hare.html

do記法で再帰モナド

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/143

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/144

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/146

相互参照がある場合は

http://www.sampou.org/cgi-bin/w3ml.cgi/haskell-jp/msg/147

遅延パターンを使えばいい

その他のツール  その他のツール - 他人のHaskell日記 を含むブックマーク

HLintは、Haskell Programの一般的な間違いを見つけだすツール

$ hlint darcs-2.1.2

darcs-2.1.2\src\CommandLine.lhs:46:1: Use a string literal
Found:
  [' ', '\t', '"', '%']
Why not:
  " \t\"%"

darcs-2.1.2\src\CommandLine.lhs:49:1: Eta reduce
Found:
  quotedArg ftable
    = between (char '"') (char '"') $ quoteContent ftable
Why not:
  quotedArg = between (char '"') (char '"') . quoteContent

darcs-2.1.2\src\CommandLine.lhs:94:1: Use concatMap
Found:
  concat $ map escapeC s
Why not:
  concatMap escapeC s

darcs-2.1.2\src\CommandLine.lhs:103:1: Use fewer brackets
Found:
  ftable ++ (map (\ (c, x) -> (toUpper c, urlEncode x)) ftable)
Why not:
  ftable ++ map (\ (c, x) -> (toUpper c, urlEncode x)) ftable

darcs-2.1.2\src\Darcs\Patch\ReadMonads.hs:61:29: Use const
Found:
  \ _ -> Nothing
Why not:
  const Nothing

Neil Mitchell - HLint

HAT (Haskell Tracer)

4: dimagekidimageki   Hat

Hat - the Haskell Tracer http://www.haskell.org/hat/

というのがあります。
Hat をインストールする

洗練された Linux distribution を使えば簡単ですが、そうでないひとは

   1. ghc http://www.haskell.org/ghc/ をインストール
   2. hmake http://www.cs.york.ac.uk/fp/hmake/ をインストール
   3. hat をインストール

の手順で導入する必要があります。

hmake は ghc でないコンパイラもサポートしていますが、hat を使うなら

ghc で十分だと思われます。

hat 2.04 の configure にはバグがあります。もしもインストールに失敗したら

    * lib/YOUR_PLATFORM/config の GHCSYM= の行を編集
          o 余計な改行をとりのぞき、 GHCSYM=604 にする
    * src/hatlib/Makefile の CPPFLAGS を編集
          o 該当行の ghcsym を cat している部分を 604 を代入することに変える

ことで、成功するかもしれません。
Hat を使う

   1. ソースを書く
         1. トレースは main 実行時に作られるので、中身を見たい関数を main で評価する
   2. 実行ファイルを作る
         1. hmake -hat progname
         2. progname はファイル名から.hs をのぞいた名前を指定すること
         3. プログラム progname ができあがるので実行
   3. hat-observe progname

hat-observe の使い方は hat のページにマニュアルがあります。

ためしに square(3+4) の例をやってみると次のようになります。

    -- Sample.hs

    main = do print (show foo)

    return ()

    foo :: Int

    foo = square (3+4)

    square :: Int -> Int

    square x = x * x

    % hmake -hat Sample

    % ./Sample

    % hat-observe Sample

    hat-observe 2.04 (:? for help, :q to quit)

    hat-observe> square

    square 7 = 49

    hat-observe>

hat のページにパターンマッチの展開の具体例が載っています。

Debug.Traceの使い方

Page not found · GitHub Pages

本物のプログラマはHaskellを使う - (2/3)第15回 Haskellでのデバッグのコツをつかむ:ITpro

Pharmd255Pharmd2552012/01/01 05:47 Hello! eeeadck interesting eeeadck site! I'm really like it! Very, very eeeadck good!

Pharme251Pharme2512012/01/01 05:47Very nice site! <a href="http://oieapxy.com/tyxqqt/1.html">cheap viagra</a>

Pharma748Pharma7482012/01/01 05:48Very nice site! [url=http://oieapxy.com/tyxqqt/2.html]cheap cialis[/url]

Pharme548Pharme5482012/01/01 05:48Very nice site! cheap cialis http://oieapxy.com/tyxqqt/4.html

Pharma917Pharma9172012/01/01 05:49Very nice site!

Pharmf968Pharmf9682012/01/26 00:21 Hello! bffeecc interesting bffeecc site! I'm really like it! Very, very bffeecc good!

Pharme82Pharme822012/01/26 00:22Very nice site! <a href="http://apxoiey.com/aoarvx/1.html">cheap viagra</a>

Pharmd37Pharmd372012/01/26 00:22Very nice site! [url=http://apxoiey.com/aoarvx/2.html]cheap cialis[/url]

Pharme874Pharme8742012/01/26 00:23Very nice site!

Pharma726Pharma7262012/06/11 01:29 Hello! ceeedea interesting ceeedea site! I'm really like it! Very, very ceeedea good!

Pharmd200Pharmd2002012/06/11 01:29Very nice site! <a href="http://opxaiey.com/oyyxxky/1.html">cheap viagra</a>

Pharmk452Pharmk4522012/06/11 01:30Very nice site! [url=http://opxaiey.com/oyyxxky/2.html]cheap cialis[/url]

Pharma535Pharma5352012/06/11 01:30Very nice site! cheap cialis http://opxaiey.com/oyyxxky/4.html

Pharmd794Pharmd7942012/06/11 01:31Very nice site!

シアリスシアリス2012/11/29 16:06シアリスの個人輸入代行http://xn--ed-gg4aodl70a.com/
シアリススーパーアクティブの主成分はタダラフィルです。勃起不全の治療を目的としたED経口治療薬です。
シアリススーパーアクティブ販売 シアリススーパーアクティブ購入 シアリススーパーアクティブ通販http://xn--ed-gg4aodl70a.com/cialis_superactive.html確実にシアリススーパーアクティブをお届けしております。

WomeraWomera2013/03/28 16:22女性用バイアグラWomeraがどのように開発されたのか
また、どのように身体、精神に影響を及ぼすのか
女性用バイアグラ Womera http://xn--cckd1b8h6e.cc/445 <a href=” http://xn--cckd1b8h6e.cc/445 ”>女性用バイアグラWomera</a> [url=http://xn--cckd1b8h6e.cc/445]女性用バイアグラWomera[/url]

lzyalgjcvmlzyalgjcvm2013/11/24 03:09qbsrcibtlfmm, <a href="http://www.ymwwdxvtwl.com/">ahunsodnwr</a> , [url=http://www.xmzlbwpuzp.com/]wvpcycknjq[/url], http://www.lowrdhehwf.com/ ahunsodnwr

zimfccgwzizimfccgwzi2014/04/13 04:11dmcmxibtlfmm, <a href="http://www.xaaivwmoek.com/">jxpcympinj</a> , [url=http://www.zchhkymuur.com/]vyptarwibt[/url], http://www.mfzkaqraij.com/ jxpcympinj