haskell-jp / questions #21

はい、なので実際のコードでは Word64 にしています。
実際のところは正確にわからないので本当は Integer のほうがいいのかもしれませんが。
ありがとうございます。katipは何かのライブラリで使われているのをみたことがあります。
わかりやすさの観点は別にして、Theorems for free の方は型の情報だけから fusion が導かれるのが興味深こうございました。
圏論的には fold はF代数の catamorphism なのでそのユニーク性から融合法則を導く方法も多分あると思います。ググったところでは cata-fusion と呼ぶみたいです。
皆さんPreludeは何を使っていますか。できれば使用感なども知りたいです。。!
https://github.com/sdiehl/protolude
https://github.com/commercialhaskell/rio
https://github.com/serokell/universum
今日 universum を使ってみたんですけど、第一印象は「かゆいところに手が届かない」Preludeでした。
@cdepillabout ずいぶん前にどこかで聞いた気がするんですが、デニスさんはたしか https://hackage.haskell.org/package/classy-prelude でしたっけ?
さいきんは rio であそんでます
lens 系演算子がもっと欲しいなぁって思う
rioはちょっと気になってるので勉強用で作ってるリポで遊んでみようかなと思ってます。
Universumすごく気まぐれなんですよね。。前のバージョンでは`containers`使えなかったのに今日触ったのは入ってたし、次のバージョンでは`text-format`が丸々削除されるらしくて、DevOpsが泣いてました。
ごめんなさいやっぱり`containers`使えませんでした。
僕がなにか勘違いしてるんですかね。。
type SomeMap = Map String int
これは`Map`を明示的にインポートしなくても定義できるのに、`Map`に関する関数は一切使えないんですよね。あと
import Data.Map.Strictができないんです。
そうですね、classy-preludeを結構使っています。いろいろ便利な機能を提供していて、mono-traversableの関数をexportしているので使いやすいという印象です。
@sugano has joined the channel
Yesod開発者なのでclassy-preludeをよく使ってますがstackもrio行ったし移行するのかなあ
classy-prelude,最近もsafe-exceptions -> UnliftIO.Exceptionの移行があったり,かなりAPI破壊してくるのが辛いですね
classy-preludeは、以前よりはマシになったとはいえ、リエクスポートも過剰で正直出来が悪いと思います
個人的には,プロジェクトでかなり使用頻度の高い関数群や要請(保守性が高い方がいい/関数が所属してるモジュールの検索性は悪くてもいいので,Utilityが欲しい/dependencyが少ない方がいい)がかなり異なるので,プロジェクトごとにclassy-preludeなりbase-preludeなりをベースにPreludeモジュール作るのが好みですね
基本は普通のPreludeなんですが、idと(.)はControl.Categoryの奴が使いたくて毎回hidingしてますね……自分に合う奴を探した方がいいのでしょうか
なるほど 後者は歯が立たないと思ったのは拙速でした 腰を据えて読み返してみれば 記法が異なるだけで 3.2 Fold は確かに Parametricity と代数の可換性から自然に fusion が導かれているのが分かりました 重ねてご教示有難うございました
@n.tatsuki.214 has joined the channel
カスタムPreludeは潜在的にバグとなりうる関数、型クラスと取り除いてるので安全なコードが書けるというのが1つの利点だと思います。(UniversumではShowの型クラスインスタンスを定義できない(導出のみ)し、NonEmptyのリストのみhead,tail等ができる)RIOに関してはいえばReaderTパターン(変換子の中でも鉄板パターン)をベースにいれています。
ただ地雷Preludeもあるので注意しないといけません。。!
昨日の https://haskell-jp.slack.com/archives/C4M4TT8JJ/p1526345560000259 で、fumiさんから助言を受けて、「型レベルリストから要素を取り除く型族Remove :: k -> [k] -> [k]」を書いているところなんです。
値レベルの関数を割と愚直に翻訳してこんな :point_down: ところまで書きました(よそのライブラリーで実装済みじゃないのか、という気もしますが)。
type family ConsUnless (b :: Bool) (x :: k) (xs :: [k]) :: [k] where
  ConsUnless False x xs = (x ': xs)
  ConsUnless True _ xs = xs

type family Reject (p :: (k -> Bool)) (xs :: [k]) :: [k] where
  Reject p '[] = '[]
  Reject p (x ': xs) = ConsUnless (p x) x (Reject p xs)

type family Same (x :: k) (y :: k) :: Bool where
  Same x x = 'True
  Same x y = 'False

type family Without (x :: k) (xs :: [k]) :: [k] where
  Without x xs = Reject (Same x) xs

で、コンパイルするとなぜか
    • The type family ‘Same’ should have 2 arguments, but has been given 1
    • In the equations for closed type family ‘Without’
      In the type family declaration for ‘Without’

というエラーが出ます。 Same が2つ引数をとるのはいいとして、 Reject の第一引数が引数を1つとる型族をとるよう宣言しているので、 Same x とすればいいんじゃないかと思えるのですが、なんでエラーになってしまうのでしょうか?
補足: 名前が紛らわしくて申し訳ないんですが、 fumiさんのおっしゃる Remove に相当するのは Without と呼んでいます。
型族ってカリー化みたいなのできたっけ?
僕はあんまり詳しく無いですが、確かこの論文に色々書いてあったような
Promoting Functions to Type Families in Haskell
https://repository.brynmawr.edu/cgi/viewcontent.cgi?referer=&httpsredir=1&article=1000&context=compsci_pubs
型族は常に引数を飽和させる必要があります。エレガントさは損なわれますが、Withoutは直接定義することになると思います
@kaznak.at.work has joined the channel
毎度ありがとうございます。
「レコードから指定したキーの要素を取り除いたレコードを返す関数」作りの続きです。
:point_down: のような、closedな型族と型クラスを組み合わせて、レコードが空の場合とそうでない場合とで実装をわけられるようにしました。
が、残念ながら今度は実装で思わぬコンパイルエラーになってしまいます(エラーメッセージは後ほどコメントに張ります)
おそらくもう一歩なところだと思うので、助言をいただきたく!
type family Result (x :: k) (xs :: [k]) :: [k] where
  Result x '[] = '[]
  Result x (x ': xs) = Result x xs
  Result x (y ': ys) = y ': Result x ys

class Without (x :: k) (xs :: [k]) where
  without :: Membership xs x -> (h :* xs) -> (h :* Result x xs)

instance Without x '[] where
  without _ _ = nil

instance Without x (x ': xs) where
  without key rec =
    let (_, ys) = huncons $ toHList rec
    in without key (fromHList ys)

instance Without x (y ': ys) where
  without key rec =
    let (y, ys) = huncons $ toHList rec
    in y <: without key (fromHList ys)

huncons :: HList h (x ': xs) -> (h x, HList h xs)
huncons (HCons hx hxs) = (hx, hxs)
エラーメッセージです :point_down:
app\Main.hs:41:21: error:
    ? Couldn't match type ‘xs’ with ‘x : xs’
      ‘xs’ is a rigid type variable bound by
        the instance declaration at app\Main.hs:38:10-28
      Expected type: h :* (x : xs)
        Actual type: h :* xs
    ? In the second argument of ‘without’, namely ‘(fromHList ys)’
      In the expression: without key (fromHList ys)
      In the expression:
        let (_, ys) = huncons $ toHList rec in without key (fromHList ys)
    ? Relevant bindings include
        ys :: HList h xs (bound at app\Main.hs:40:13)
        rec :: h :* (x : xs) (bound at app\Main.hs:39:15)
        key :: Membership (x : xs) x (bound at app\Main.hs:39:11)
        without :: Membership (x : xs) x
                   -> (h :* (x : xs)) -> h :* Result x (x : xs)
          (bound at app\Main.hs:39:3)
   |
41 |     in without key (fromHList ys)
   |                     ^^^^^^^^^^^^

app\Main.hs:46:26: error:
    ? Couldn't match type ‘ys’ with ‘y : ys’
      ‘ys’ is a rigid type variable bound by
        the instance declaration at app\Main.hs:43:10-28
      Expected type: h :* (y : ys)
        Actual type: h :* ys
    ? In the second argument of ‘without’, namely ‘(fromHList ys)’
      In the second argument of ‘(<:)’, namely
        ‘without key (fromHList ys)’
      In the expression: y <: without key (fromHList ys)
    ? Relevant bindings include
        y :: h y (bound at app\Main.hs:45:10)
        ys :: HList h ys (bound at app\Main.hs:45:13)
        rec :: h :* (y : ys) (bound at app\Main.hs:44:15)
        key :: Membership (y : ys) x (bound at app\Main.hs:44:11)
        without :: Membership (y : ys) x
                   -> (h :* (y : ys)) -> h :* Result x (y : ys)
          (bound at app\Main.hs:44:3)
   |
46 |     in y <: without key (fromHList ys)
質問があります。

練習のために、 stack resolver lts-11.9 の環境で数独に関するライブラリを作っています。
いま、一つのセルの状態を表現するために Maybe を使おうと思ってます。
例えば、あるセルに 1 が入っていれば Maybe 1 、空ならば Nothing という具合です。

ただ、そのままでは show した時に Maybe や Nothing が表示されてしまうので、
この表示を変えたいと思っています。
例えば、 Maybe 1 ならば 1 、 Nothing ならば _ が出力されるという具合にです。
出来れば read も同じように出来ると嬉しいです。

Maybe 関連のライブラリ関数を使いたいので、さしあたって show と read の振る舞いだけ変えたいのです。
こういった、ほとんど同じなんだけど、ほんの少し振る舞いが異なる型を表現したいときはどうしたら良いのでしょうか。

よろしくお願いします。
newtype Cell = Cell (Maybe Int)

ですねぱっと思いつくのは。

newtype とすることで、 Cell はまさにおっしゃるような、 「 Maybe Int と ほとんど同じなんだけど、ほんの少し振る舞いが異なる型」として定義できます。
ただ、個人的には Show はあくまでもデバッグ用の文字列に変換するものとして使ったほうがよいのではないかと考えています。
なので、 newtype でセル専用の型を用意するにせよしないにせよ、
ShowRead のインスタンスを無理に書き換えずに、文字列からパースして作成したり文字列に変換する用の関数は独立して作った方がいいんじゃないかと思います。
あんまりよくわかってないですが,
instance Without x ys => Without x (y:ys) where

が正しいのではないでしょうか?(Overlappingしそうですが)
prettyprinter https://hackage.haskell.org/package/prettyprinter は、名前の通りShowよりも小奇麗に表示するのに適しています。整列表示などもできるのでお試しあれ
@igrep pinned a message to this channel.
文脈を把握してきたんですけど,うーん僕はあまり型レベルプログラミングには詳しくないんですが,hFilterは結構実装難しいんじゃないんですかね?上のエラーは,`Without x xs =>` の制約がないため,xsに対してxのwithoutメソッドを呼べないエラーですが,おそらく呼べるようにしてみても
type family Result (x :: k) (xs :: [k]) :: [k] where
  Result x '[] = '[]
  Result x (x ': xs) = Result x xs
  Result x (y ': ys) = y ': Result x ys

のResult x (y ’: ys)の部分がかなり怪しくって,こいつをpolymorphicなままで呼び出すのはかなり技巧が必要だと思いますね
まあ少なくとも,上の例ほど単純には書けないんじゃないかな?と思います(なんらかの,type equalityをラップする処理が必要なんじゃないかなと思います)
ありがとうございます。newtype 使うといい感じに書けました。

ただ、個人的には `Show` はあくまでもデバッグ用の文字列に変換するものとして使ったほうがよいのではないかと考えています。

これはなぜなんですかね? なんとなく Show や Read は標準のリーダ/ライタなのかなと思っていたのですが。

prettyprinter ありがとうございます。見てみます。
なんか,知識が錆びついてたみたいですね.昔は一苦労必要だった気がするんですが, https://hackage.haskell.org/package/base-4.10.1.0/docs/Data-Type-Equality.html#t:-61--61- を使えば結構簡単にいけましたね.FunctionalDependenciesを使うといけると思います(HListがその方式っぽいですね)
いちよ,書いたやつを置いときます(ただHListを参考にやって見た方が,分かりやすいかもしれません.あと,extensibleよく知らないんですがMembershipが内部で持ってる位置をfilter後に移してやる必要があったりするんじゃないんですかね?)
https://gist.github.com/mizunashi-mana/df1d54e87a9901b9f225f1ed658bdff6
コードがちょっとミスってたんで,微修正しました
絶対にそうした方がいいというつもりではないですが、
例えば Stringshow 関数は、受け取った文字列をそのまま返すのではなく、
ダブルクォートで囲ったり、エスケープシーケンスがわかりやすくなるように変換したりして返すようになっています。
これと同じように、大概の Show のインスタンスは、
変換後の文字列から、何の型の値を変換したかがわかりやすいように作られています。
traceShowId など Show のインスタンスをデバッグ用に使う関数に渡して表示させた際、(特に何カ所も出力させた場合に)どの型の値を出力したのか区別できるようにした方が、視認性が上がるためでしょう。
なので、自前で定義するにしても、何の型の値から変換された文字列かがわかりやすいように作った方がよいかと思います。
なるほど。 Show と Read はあくまで ghci 用だと考えたほうが良いってことですね。確かにそんな気がしてきました。ありがとうございます。
Readを自前実装しようとするとその型のリストをRead対応させるのがめっちゃ面倒という問題もあります
そうなんですよねぇ。
ただ、(おそらく)今回のように1アプリケーションでしか使わないデータ型であれば、あえて Read のインスタンスにする必要はないだろうな、と思って触れてませんでした。
ライフゲームを作りたいんですが、そのためのGUIライブラリでいいものはありますか?
僕はglossをよく使ってます!

ライフゲームならこの simulate って関数を使えば楽に実装できるんじゃないかなと思います~!
早い返信ありがとうございます!想定している機能は停止/再開ボタンとクリックによるセルの反転が含まれているので すが、それもまた play で実現できそうですね!
はい、playで実現できると思います :raised_hands:
glossを依存関係に加えた後にビルドしようとしたら、freeglutをインストールしていなかったために推移依存関係によるGLUTのビルド時に何かが壊れたようで、precompiled packageが全部読み込めなくなったみたいです。
glossを依存関係に加えたプロジェクトをstack buildでビルドした後に実行しようとしたらuser error (unknown GLUT entry glutInit)が出たため調べたらglutのインストールがいるよと出たため、一旦やめて他のプロジェクトをstack buildでビルドしようとしたら