haskell-jp / questions #81

いえ 何もしてないのにいつのまにかこうなってしまったので……それにいくら再インストールしても同じ問題が発生しちゃうんですよ もう手詰まりで
何のコマンドを実行したら発生したんですか?それともコマンドじゃなくてエディターを起動したときとか?
あと、GHCはどうやってインストールしましたか?
@eagle has joined the channel
Takashi NIIJIMA
@Takashi NIIJIMA has joined the channel
この件に関する回答を募集しています。
調べてもなかなか分からなくて…
https://github.com/haskell/bytestring/blob/95fe6bdf13c9cc86c1c880164f7844d61d989574/Data/ByteString/Builder/Internal.hs#L348-L353
bytestring パッケージの Builder の実装ってどうしてこうなっているんですか? 予想としては差分リストと BufferRange を状態に持つアロー的な何か ( http://hackage.haskell.org/package/arrows-0.4.4.2/docs/Control-Arrow-Transformer-CoState.html ) の組み合わせだと思っています。
おひさしぶりです

Haskellで、repl付きのinterprerを実装するときの話です。
以下の、HaskellでSchemeを作るサイトでは、inteprerの環境の型にIORefを使用しています。
https://ja.wikibooks.org/wiki/48%E6%99%82%E9%96%93%E3%81%A7Scheme%E3%82%92%E6%9B%B8%E3%81%93%E3%81%86/%E5%A4%89%E6%95%B0%E3%81%A8%E4%BB%A3%E5%85%A5


ここで3つ質問があります。
1. なぜStateが適していなくて、IORefが適しているのか
2. IORefを使った場合にenvを持ち回らない方法はあるのか
3. Stateを使ったときにreplが正しく動かないのはそういうものなのか、単純に僕の実装が悪いのか
特に、1,2についてお聞きしたいです。
質問はさておき、最終的にやりたいことは「envを持ち回らずにreplでevalを実行すること」です


1に関しては、上記のページの上の方に解説がありますが、理由をよく理解できませんでした。
関数の呼び出しの際にenvが入れ子になっていくのはわかりますが、そのときになぜStateではだめで、IORefならうまく機能するのかがわかりません


2に関して、上のサイトを少し参考にして実装していっているものが以下です。IORefを使っていてeval関数はenvを引き回しています。StateモナドやReaderモナドを使うときのようにenvを隠蔽する必要はあるのでしょうか
https://github.com/mrsekut/Hytl/blob/86cca753fc/src/Eval.hs


3に関して、「これIORefじゃなくてStateにすればenv隠蔽できるやん!」と早とちりして途中まで実装したものが以下になります。Stateを使うことでenvを持ち回る事がなくなりましたが、replで動かしたときに変数呼び出しがうまく機能しません。というのも、一行入力する毎にStateあるEnvが空になっているようです。これは単に僕のreplの実装が悪いだけなのか、IOが絡む場合はStateだけではうまく動かないのか、というのをお聞きしたいです。
https://github.com/mrsekut/Hytl/blob/2bbf7c4058/src/Eval.hs



よろしくおねがいします。
取り急ぎ。
なぜStateが適していなくて、IORefが適しているのか
例外が発生したときに、途中まで更新していたstateを保存する方法がないためです。
ちなみに、`StateT` を重ねたMonad stackが MonadUnliftIOのインスタンスじゃないのも同じ理由です。

IORefを使った場合にenvを持ち回らない方法はあるのか
ReaderT を使いましょう。
ここまでのことはいわゆる「ReaderT Design Pattern」の原典である https://www.fpcomplete.com/blog/2017/06/readert-design-pattern でも触れられています。
@igrepさんが言ってるのは一般的な State の問題点でここでの問題点としてはかなりズレてると思います

今回 IORef を使用することのポイントは、式を評価する際環境を容易に変えられ、かつ環境を可変に保てるという点だと思います。State では強制的に全環境が共通のものになってしまいますが、IORef を使った場合は eval に渡す環境を変えるだけでクロージャの評価にも流用できます。それがおそらく元文献が言いたいことだと思います。

ところで、通常 Env の引回しを明示的にしたくなくて、でもたまに局所的に環境を変えたいという需要が、Reader モナドで実現できるというのは同意です。Eval = ReaderT Env IO とすれば前者はそのまま実現できて、後者は Reader の local メソッド で実現できます

ところで3番目のはコメントアウトしてる部分は型が合って内容に見えるんですが、変数の参照はどうやって実装したんでしょう?
実際REPLの実装にStateを使ったら間違いなく該当する問題にハマりますし(だから、Haskelineは ReaderT IO を使っている)、挙げている文献を読まず意図を汲まなかったのは私の落ち度ですが、「ずれている」という言い方はちょっと引っかかりますね...。 :confused:
@igrep すいません、言葉選びが悪かったですが、上の「ずれている」は全く役に立たないと言っているわけではなく、<@UCM4G8PGW>さんに対して元文献で言ってることとは話が違うと言うことが分かるようにという意図での発言でした。元文献で言ってること以上に State ではなく Reader を使う意義があるというのは、私も同意です
BuildStepはADT版Freeのように操作を再帰的に繋げたコルーチン的構造で、そのままだと結合が遅いので差分リストのようにしているのだと理解しています
頭から抜けていたんですが、操作というのが先でした。この理解であっているみたいですね。全称量化されている r は ST モナドのような操作を制限するためのものですかね?
igrepさん、mizunashi-manaさん、ご返事ありがとうございます。なるほど、Readerを使うのが良さそうですね。僕の実装ではまだ例外処理を書いていないのでStateを使ったときの問題に出くわしていなかったので気づけてよかったです(この辛さもまた試してみたいですが)


実装するときに以下のような経過をたどりました
IORefが良いらしい→できた→env消したい→Readerを使うと良いらしい→Readerつかう→え、これStateじゃないとムリでは?→Stateつかう→replが動かんくなった、ムリなのかなやっぱりIORefにするか→env復活するじゃん嫌だ→質問

3番目のリンクのコメントアウトの型がずれているのはこの経過で前の段階のものが残っているからです

Readerのlocalなどに対する理解が甘かったようです。Readerの理解と実装を今晩トライしてみて躓いたらまた質問させていただきます。
補足ですが、コメントで言っている Reader を使うとは IORef を使った Env を Reader で渡すということです。Reader 単体では読み込みしかできないので、環境への書き込みが行なえませんが、IORef を読み込むことで可変にできます
あえてStateTモナドで実装するなら、runEvalの型を`Exp -> StateT Env IO Int` として、Repl.hsの方でevalStateTしてやる感じになるかと思います。evalStateTを抜けたら状態は消えてしまいますので。
同じモジュールにあるPutモナドの実装との兼ね合いなのか、結果を持たせられるようになっているようです
@myamamura has joined the channel
@kuono has joined the channel
@paulko has joined the channel
@Izawa has joined the channel
お久しぶりです。
指定した時刻までスレッドを休止してくれる関数をご存知ですか?
threadDelayは、指定した時間間隔になりますが、例えば毎分05秒に処理をしたい、というような場合は「次の分の05秒 ー 現在時刻」の秒数を計算してセットしてあげる必要があります。
それでもよいのですが、既存の関数があれば使いたいです。
https://hackage.haskell.org/package/cron-0.6.2/docs/System-Cron-Schedule.html が割とメンテされている方です。
ただこれ、もともとcronの時刻指定の文字列をパースするという目的のパッケージだったためか、単に「指定した時刻に実行する」という目的にはオーバーキルなんですよねぇ :confused:
なので、実はちょうど私も同じようなニーズがあったこともあり、一部の関数をexportするよう提案しています。
https://github.com/MichaelXavier/cron/issues/43
@Kuroda has joined the channel
お久しぶりです. の記事を執筆したのですが,出力した列が素数列になりませんでした.
```>> map p [1..10]
[2,3,5,7,11,13,129,172,172,172]```
`p`が今回実装した関数で,n番目の素数を計算します.
自分の予想では`e2`の中で `(** (1.0 / fromIntegral n))` を適用する過程で丸め誤差が起こっているような気がしたのですが,~(そもそも `(** (1/7)` )ってそんな誤差出るでしょうか... )~ 間違えてるのか限界なのかわからず助言をいただきたいです.よろしくお願いいたします.

簡潔にまとめると以下です.
1. 予測は合っているか.
2. 合っていればより精度の良いn乗根の関数は存在するか.
3. 他に誤りが原因である場合,指摘して欲しい
詳しく見れてませんが分数で誤差が出るならRatio型を使ってみるのも手なのかなと思いました:eyes:
あ、e0をウィルソンの定理そのままで書き換えたら動いたので cos の中で小数に変換するところで何か起こってそうですね
e0 :: Integer -> Integer
e0 i = floor $  if (product[1..i-1] + 1) `mod` i == 0 then 1 else 0

> map p [1..10]
[2,3,5,7,11,13,17,19,23,29]

多分 cos や pi の精度ってそれほど良くないので、大きい数ほど本来の値とズレていって、本来 1 と出すはずが 0 を出すと言うのが積み重なった結果だと思いますね
ちょうど 7 番目で e0 の結果が 1 になるはずが 0 になったりしてませんかね?(後で試そうと思ってます)
なるほど..cos や pi の精度を気にしてませんでした.., 確かにe0をmod使った式にすると治りますね...やはりそこはプログラミング言語らしく書く必要がありそうですね!ありがとうございます.
あれ,`e0` って1~2^7のどこまで試すと良いんでしたっけ..脳が疲れてしまったので自分も明日やってみます...
既存の関数というと標準のライブラリということでしょうか?
そうであればGHCの標準ライブラリにはそのような関数はないですね…
@teruchi has joined the channel
@ has joined the channel
@shinichi has joined the channel
@wshito has joined the channel
@n_odoki has joined the channel
@shimbaco has joined the channel
GHCi で type constructor の role を確認する方法はありますか?
> data Hoge = Fuga 
> :i Fuga 

で定義情報はみれそうですが、質問の意図とは違うのかな?
role とは、 の仕様のことを指しています
:set -XTypeFamilies
:set -XGADTs
type family F a
data T a b c = MkT b (F c)

:set -XTemplateHaskell
import 
$( stringE . show =<< reifyRoles ''T )
-- "[PhantomR,RepresentationalR,NominalR]"

とかはどうでしょうか?
なるほど、ありがとうございます。GHCi の機能としてはないですよね? (無いなら機能追加を提案しようと思ってます)
探してみた感じではなさそう、

と思ったんですが

ghci> :i T
type role T phantom representational nominal
type T :: * -> * -> * -> *
data T a b c = MkT b (F c)
        -- Defined at <interactive>:4:1

なんか表示されてますね…
あれ、それって GHC バージョンいくつでしょう? (うちの 8.8 の環境だと表示されませんでした...)
>ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.10.1

でした。