haskell-jp / questions #89

Haskell の head / tail は単連結リストの言葉ですね。Haskell のリストはそのまま単連結リストなので、単連結リストの先頭を示す head とそれ以降を示す tail という言葉を関数名にそのまま流用したものです。

確かにコマンドの head / tail を想定すると、ギョッとする命名かもしれませんね
ちなみに、 head :: [a] -> Int -> [a] に相当するものとして take があります(引数の順番は逆なので注意)。
それを使って first みたいなものを作れば良かったんじゃない、という意図なのかも知れませんが。
head はなくてはならないものかというと必ずしもそういうことはなくて、 xs :: [a] について、 let (hd:tl) = xs in (hd,tlを使った式) とすれば先頭の要素とそれ以外を取り出せます。
ご回答ありがとうございます。

@
単連結リストは日本語でいうところの単方向リスト(Singly linked list)でしょうか?

@igrep
少し話が逸れますが、 take がその順番になってる理由は、取る数よりリストの方が適用する種類が多いからそうなってるんでしょうか?
はい、そうだと思います。
LensのPrismの使い方について質問です
Prismを setoverASetter として使えることは分かったのですが、Prismの中身に Lensを使用してアクセスしたいと思った時にやり方がわかりません。(例を下に載せます)
現在は↓の makeBelongTo のようにしているものの、Characterの値コンストラクタが変わったら変更しなくてはいけなくなり、じゃあPrismにしたメリットないのでは、と悩んでいます。
data Character = Animal {
                    _name  :: String
                  , _age  :: Int
                  , _memberOf :: Maybe String
                  }
               | Object {
                    _name :: String
                  , _memberOf :: Maybe String
                  }
              deriving (Show)

makePrisms ''Character
makeLenses ''Character

-- この関数を
makeBelengTo :: String -> Character -> Character
makeBelengTo belongTo c@(Animal n a _) = Animal n a (Just belongTo)
makeBelengTo belongTo c@(Object n _)   = Object n (Just belongTo)

-- こんな感じにしたい(これだと動かない)
makeBelongToWithLens :: String -> Character -> Character
makeBelongToWithLens belongTo = set (_Animal.memberOf) (Just belongTo)
                              . set (_Object.memberOf) (Just belongTo)

また、
set (_Animal._3) (Just belongTo)

のような解法は見つけたのですが、結局タプルの位置を指定しているためこれならPrismのメリットがないな…と思っています。
外泊中なので試せないのですが、普通に set memberOf (Just belongTo)c{_memberOf=Just belongTo}で行けないでしょうか。
あっ確かに…
実際に書いているコードでLensの名前が違ったのでこんな書き方にしていましたが、よく考えたら同じ名前にすれば大丈夫でした…(違う名前にする必要がなかったです)
それを元にして考えたら他の部分もうまく行きました!ありがとうございます
@SF has joined the channel
@hos has joined the channel
megaparsec 固有の質問なのですが、`Text.Megaparsec.Char.Lexer.lineFold` ができることは同モジュールの indentBlock と同じなんでしょうか?`indentBlock` でできなくて lineFold ならできることがある?単に書き味の違い?

Hackage
https://hackage.haskell.org/package/megaparsec-8.0.0/docs/Text-Megaparsec-Char-Lexer.html#v:lineFold
https://hackage.haskell.org/package/megaparsec-8.0.0/docs/Text-Megaparsec-Char-Lexer.html#v:indentBlock
チュートリアル
https://markkarpov.com/tutorial/megaparsec.html#indentationsensitive-parsing
どっちにしろこれを使って Haskell のオフサイドルールをパースするのは難しいかなぁ。言語レポートに掲載されてるアルゴリズムと等価なパーサーになってるかの保証が難しそう。
Data.Yaml のパーサーコンビネーターの作り方について質問です
decodeFileEitherなどで読み込んだYamlの中にあるパスをさらに読み込んでパースがしたいのですが、一度他の型を経由させる方法しか思いつかなくて迷っています。
パーサー(`Parser a` )内部でIOを使うことは可能でしょうか(パース結果をFilePathとして、そのファイルも読みにいくことは可能でしょうか)
runParser 内部にある f がどうにかできないのかなと思っているのですがよくわからず…
何かいい方法があれば知りたいです…

例としては:
data:
  - path: foo.yaml
    offset: (0, 1)
  ...

というファイルを decodeFileEither で読んだ時、`foo.yaml` も読み込んで結果に含めたいです。

現在は一度しか入れ子にならないため、一つ中間用の型を用意してその型に(パスはStringとして保存して)変換した後、その中にしまったパスをさらに decodeFileEither で読んでいます。


data ActualData = ActuallData ...

data IntermediateData = IntermidiateData { path :: FilePath, ... }

load :: FilePath -> IO (Either ParseException ActualData)
load fp = do
  intermediate <- decodeFileEither fp
  includedData <- decodeFileEither $ path intermediate
  return -- intermediate と includedData を使用して、ActualData を生成する

実際に使っている例だとこれになります。( Face をデコードするために、一度 FaceFile としてデコードした後に読み直している)
https://github.com/Cj-bc/faclig/blob/master/src/Graphics/Asciiart/Faclig/Types.hs#L36-L57

しかし、

• 途中に他の型をできれば挟みたくない
• 入れ子を何重にもしたいので、一度で解決できるようにしたい
という理由があり、なんとかできないのかなと悩んでいます。
パーサー(`Parser a` )内部でIOを使うことは可能でしょうか
少なくとも、これはできないと思います。
ここでの Parser ahttps://hackage.haskell.org/package/aeson-1.4.6.0/docs/Data-Aeson-Types.html#t:Parser で、 IO を含まないので。
HKDにしてYAMLをパースした結果
として Record IO を返すようにするか、そこまでしなくとも IO 型の値をフィールドに含めるのはいかがでしょうか?
HKDについては https://qiita.com/thimura/items/85bdeeca6ced74c89478 とか http://fumieval.hatenablog.com/entry/2019/12/25/224329 を。
同じ議論が https://haskell-jp.slack.com/archives/C4M4TT8JJ/p1554720210064400 でもありましたね...
確かにこのスレッドへの返信の通りreflectionパッケージを使うという手もありますね。
Recordについては全く知らなかったので調べてみます!
フィールドにIO型を含めた場合、最終的にはIOを取り外す処理は一括でできるのでしょうか…?(各フィールドの値をそれぞれ取り出す必要があるように思えているのですがそうでもない?)
とりあえず、いただいたリンクとrefrectionを調べてみます!
最終的に IO (Parser a) みたいにできるんじゃないかな、HKD なら
最終的にはIOを取り外す処理は一括でできるのでしょうか
えぇ、その部分はbarbiesに任せられるはず。そこを一括でできるのがbarbiesの強みなので
Parser (IO a) (もっと正確には、 Compose Parser IO a )から IO a に変換して最終的に Identity a として取り出せるようになるかと。
色々教えてくださりありがとうございます!
ゆっくり噛み砕いてみます
@reiwa2020 has joined the channel
@pommy has joined the channel
@ has joined the channel
@ has joined the channel
@ has joined the channel
Haskell で書いたプログラムをウェブページの上で動かしたいのですが、どうすればいいのでしょうか? 前から知っていた GHCJS は開発が停止してしまっているようです。
状況を考えると質問をした後に思い出した Asterius しか選択肢がなさそうなので、それを試してみます。
@Arakur has joined the channel
class method を inline するにはどうすればいいでしょうか?
調べた限り、instance 宣言の際に INLINE プラグマを書けばよさそうです。
しかし、プロファイルを取ると、その method がボトルネックとして現れるので、inline できてないようです。

具体的には、以下の read8 を、具体的な型 ReadBuffer に対して呼び出しています。

https://hackage.haskell.org/package/network-byte-order-0.1.5/docs/src/Network.ByteOrder.html#read8

関数定義をトップレベルに移してみたり、関係ないとは思いますが SPECIALIZE を書いたりしてみましたが、やはりうまく行きませんでした。
read8 を使用している箇所が多相になっているためにコンパイル時に型が特定できてない、というケースだと思うので、`read8` を 実際に呼んでいる箇所を一つずつ検証することになりますね... 呼んでいる関数をインライン化するとか。
(関連: https://blog.miz-ar.info/2016/06/writing-efficient-program-with-haskell/#2.specialization すでに読んでいる記事でしたらすみません)
「具体的な型」と書いたように、多相にはなっていません。
INLINEプラグマを指定していれば、意図的に最適化を切ったりしないかぎりインライン化されるはずです。もしかしたらプロファイリングが影響しているかもしれません(トップレベルに絞ってみてはいかが)。また、無関係ですがread16などはインライン化がなく辞書渡しになるので、かなりパフォーマンスが落ちます
read16 に SPECIALIZE を書いた方がいいってことですね?
ユーザーがReadableのインスタンスを追加できるので私ならINLINEをつけますが、それが想定した使い方でなければSPECIALISEでもいいと思います
ありがとうございます。INLINE を付けておきます。
@watanany has joined the channel
この質問に答えようと思ったのですが、ちょっと自信がないのでツッコミをいただきたいです。いろいろ断言しちゃっていいか迷う...
replyに回答案を書きます。
https://ja.stackoverflow.com/questions/70079/%E3%83%A2%E3%83%8A%E3%83%89%E5%89%87%E3%82%92%E5%B4%A9%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86%E4%BE%8B%E3%81%8C%E7%9F%A5%E3%82%8A%E3%81%9F%E3%81%84
------ 以下回答案 ------
コメントにも回答にも明確に回答されてないとおぼしき部分について回答します。

> つまりどこの部分にどのように注意してコードを書けばいいのかというようなことが気になっています。おそらく、instanceを実装しているときですよね?
>
> instanceの実装さえミスがなければ fやg はモナド則を満たさなくなってしまうことと関係ないと思っているのですがいかがでしょうか?

ご認識のとおりです。
Monad則はあくまでも`>>=`や`return`についての規則であるため、`>>=`や`return`の**引数が**、つまり`f`や`g`がどうなっていようと関係ありません。

が、そもそもあなたが自分でMonadのインスタンスを定義する必要さえないでしょう。
世の中には無数のMonadが存在するように見えるかも知れません(し、広い意味のモナドは実際そうなのでしょうが)、Haskellプログラミングにおいて役に立つMonadは、数えるほどしかありません。
実際のところ[モナドの六つの系統[Functor x Functor] - モナドとわたしとコモナド]((分類方法そのものは一般的ではないですが)。

世に広まるパッケージ見ていると、一見先ほどの記事にリストアップされていないMonadが見つかることがあります。
ところがそれらはほぼ間違いなく、ここにリストアップされているMonadの単純なラッパーか、それらを組み合わせて別の名前を付けたり、何らかの用途に特殊化したものです。
独自に定義しているものがあったとしたら、それは恐らく効率のためであって、実質的な役割は先ほどの記事にリストアップされたMonadのうち、どれかに当てはまるはずです。

もしリストアップされているものにどれにも当てはまらないものができれば、別途論文が書かれていると思います(というのも、実は「どれにも当てはまらない」のかどうか個人的にわからない[Select Monad]()。
とにかく、「Haskellプログラミングに役に立つMonad型クラスのインスタンス」というのはそれぐらい貴重なのです。
(もっと広い意味の、本来の圏論における「モナド」はいろいろあるかも知れませんが、私は圏論には詳しくないのでその点は突っ込まないでください!)

少し話がそれましたが、そうした事情があるため、「モナド則を守れているかどうか」というのを意識する必要があるのは、少なくともそうしたライブラリーを作るようになったら、であって、純粋にMonadのユーザーである限りその必要はありません。
それはほとんどのHaskellプログラマーが経験しないことのはずです。
仮に経験したとしても、多分にそれは(先ほど触れたような)既存のMonadを元に独自定義する場合であって、大抵はコピペと少しの修正で済むでしょう。
DRY原則に従うなら、そんなことしたくないですよね?
@knight-rose has joined the channel
高度な話題なので付いていけないですが、回答がついていなかったのでコメントさせて頂きます:bow:

> > つまりどこの部分にどのように注意してコードを書けばいいのかというようなことが気になっています。おそらく、instanceを実装しているときですよね?
> >
> > instanceの実装さえミスがなければ fやg はモナド則を満たさなくなってしまうことと関係ないと思っているのですがいかがでしょうか?
>
> ご認識のとおりです。
> Monad則はあくまでも`>>=`や`return`についての規則であるため、`>>=`や`return`の**引数が**、つまり`f`や`g`がどうなっていようと関係ありません。
上記に関して、私も同様の理解です。

上記の記事の質問者さんはモナド則を崩せる方法を知りたがっているようですが、すでに回答があるようにモナド則を崩す実装は簡単にできると思います。
プログラマがモナドを自作するときに気をつけるべきことは、モナド則を崩す実装について考えることではなく、モナド則を満たした実装になっているかに気をつけることかと思います。
モナド則を満たさない実装はあらゆるパターンが考えられますし、モナド則を満たしていない時点で、それはモナドではないため、一般のプログラマがここを深く追求する価値があるとは(今の私のレベルでは)思えません。モナドに代わる新しい計算の概念を探そうとしている方には研究対象になるのかもしれませんが。

後半のモナドの分類の言及については私の手に余りますので、コメントは控えます。。

> ご認識のとおりです。
> Monad則はあくまでも`>>=`や`return`についての規則であるため、`>>=`や`return`の**引数が**、つまり`f`や`g`がどうなっていようと関係ありません。
ただ、上記の回答だけでも、質問者さんへの回答にはなっているかと思いました。
ただ、上記の回答だけでも、質問者さんへの回答にはなっているかと思いました。
そうですね... ただこういう質問自体にうんざりしてまして。
みんな難しそうに言っているけど、そもそもほとんどの人は気にする必要さえないよ、というのはどうしてもこの場を借りて表明しておきたいのです。
@Ryo Haruyama has joined the channel
質問の主旨と全然違うのですが、WriteBufferのread8を見た感じ、これがボトルネックになるくらい呼ばれるなら、個数指定で読んでまとめて返すようなAPIが欲しいかも、と思いました。
@NorthPole has joined the channel
@Paul has joined the channel
@Eiken7kyuu has joined the channel
@ has joined the channel
@ has joined the channel
@ has joined the channel