haskell-jp / beginners #25

@匿名希望S has joined the channel
@ has joined the channel
@ has joined the channel
@hkmzk has joined the channel
@Zac Z has joined the channel
Reminder: beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。 Haskell-Beginners ML や IRCの#haskell-beginners  や RedditのMonthly Hask Anythingのような位置づけを意図しています。 beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。 • それはくだらない質問だ → くだらない質問など無い • その質問は以前にもあった → 質問者はそんなこと知らない • Google検索せよ → 検索できないから質問している beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。 • : とは何のことですか。 • タプルとは何ですか。
@fg has joined the channel
はじめまして、モナドという単語のカッコよさにあこがれて、すごいHaskell本を読んで、楽しくなってきたので、
プログラミングHaskell第2版を現在読んでいます。どうぞよろしくお願いします
@てぴか has joined the channel
夜分遅くに失礼します。
初めまして、最近Haskellに興味を持ったてぴかと申します。
Haskellを独学で学んでいるのですが、個人の学習となると、
どうしても詰まってしまう点が多いので、Haskellの仲間が欲しいと思い参加することにしました。

Haskellに関することで、もし分からないことがありましたら、
今後このSlackで質問することがあるかもしれません。
その時はまた宜しくお願いします。
早速相談したいことが出たので、質問させて下さい:bow:
stack new stack240529というコマンドで新規プロジェクトを作成したのですが、
新規作成したコードをVSCodeで開いたところ、
./app/Main.hsの3行目でimportエラーがでてしまっています。
こちらの件、何か解決方法とかあるのでしょうか?

参考までに`stack`と`hls`はghcupというツールを使ってインストールしました。
... Replies ...
うーん、単純にビルドができてないからだと思いましたが(私自身似たような経験があったので)、違う、と。分かりませんなぁ
恐らく、ghcupがインストールしたGHC( PATH に入っている方のGHC)とstackがインストールしたGHC両方があるせいで混乱しちゃってますね(関連: https://docs.haskellstack.org/en/stable/Stack_and_VS_Code/ )。
VS CodeがGHCupのGHCを使おうとして、stackはstackのGHCを使おうとしているせいで結果が食い違っているのではないかと思います。
取りあえずの解決策としては、 stack path --global-config を実行するとstackの設定ファイルへのパスが出てくるので、そのファイルを編集して、
install-ghc: false
system-ghc: true

と付け加えてください。
@mn has joined the channel
@ has joined the channel
こんにちは。このコミュニティに参加できて光栄です。2週間ほど日本を訪れています。初めて日本に来ました。

Hello. Honored to join this community. Visiting Japan for a couple of weeks. First time here.
... Replies ...
:information_source: @here 急な通知すみません。
*TL;DR 6/23 午後に Yeoh Kim Eeさんを招いて小さな勉強会を開催しようと考えています。若干名の参加者と、場所を募集しています。*

Yeoh Kim Ee さんは Lambda Indonesia という勉強会の主催で、今年の ICFP Tutorials にて Haskell についてのワークショップを開催される予定です。

:point_up: の通りこの度日本に2週間滞在される予定です。良い機会なので日本のHaskellerに会いたいということで、急ではありますが、現状次のようなプランで小さな勉強会を開催しようと思います。

• :house: *場所*(候補): Tatami Works (1人2000円の使用料がかかる見込みですが、比較的安価で、知る限り土日で複数人でも空いている見込みがあるため。ただ、これから(人数が大まかに決まり次第)予約の相談をするため、利用できない場合などに備えて*他の候補*をご提案頂けるとありがたいです。とりあえず長年 Haskell-jp もくもく会でお世話になっていた朝日ネットさんにも声を掛けようと思います。
• :walking: *参加者:* 若干名募集します。Yeohさんは日本語は挨拶くらいしか通じないそうなので、(身振り手振り、ソースコードなどを駆使しつつ)英語 :gb: でやり取りできることが望ましいです。後、特に時間を切って発表枠などを設ける予定はないですが、何かしらHaskell関係で話したいネタがあると助かります。なければ多分私やYeohさんがやってきたこと共有しながら作業する、みたいな感じで進めることになるかと思います。
Kota Mizushima
@Kota Mizushima has joined the channel
@ has joined the channel
@Nyankonen has joined the channel
@Ori Pe'er has joined the channel
@ has joined the channel
@satler has joined the channel
関数モナドについて調べてみたのですが、理解できなかったので質問させてください。

instance Monad ((->) r) where
    return = const
    h >>= f = \w -> f (h w) w

これを使用したときに以下のようになると思います。
ghci> f = (+1) >>= \a -> (+2) >>= \b -> return (a+b)
f :: Num b => b -> b
ghci> f 1
5
it :: Num b => b

上記の場合 h w(+1) 1 に置き換えられると考えています。
同様に、`f (h w) w` を単純に置き換えると (+2) ((+1) 1) 1 になってしまいますが
おかしいのは理解できています。

これはどのように考えたらよいのでしょうか ?
... Replies ...
モナドでの <- の挙動について質問させてください。

State s モナドを例にとります。
newtype State s a = State { runState :: s -> (a,s) }

instance Monad (State s) where
    return a = State $ \s -> (a, s)

    (State h) >>= f = State $ \s ->
        let
            (a, newState) = h s
            (State g) = f a
        in
            g newState

pop = State $ (\(a:s) -> (a,s))

上記の定義に対して以下のようなプログラムを実行します。
steps :: State [Int] Int
steps = do
    a <- pop
    return a

f = runState steps [1..5]

結果は以下の通りです。(hugs)
Main> :set +t
Main> :l b.hs
Main> f
(1,[2,3,4,5]) :: (Int,[Int])

動作自体は理解できたのですが、`a <- pop` により値 (Int) が束縛される理由がわかりません。

Maybe モナドで考えて a <- Just a の場合は Just が外れて a が取り出されている。と理解しやすいです。
また、`(a, s) <- pop` であった場合は Monad (State s) の定義と矛盾するような気がするので、違うのかと考えています。

モナド値 State s a の場合、`a` の位置にあるものが <- により取り出される。
のだと思いますが、そういうものと考えてしまって良いものでしょうか ?
... Replies ...
@shingo has joined the channel
困っている。ということではないのですが、質問させてください。

関数に引数が適用され、展開されていく様子を見られるようなツールのようなものは存在しますか ?
具体的には、以下のようなものがあったらと思っています。

[入力]
foldr (+) 0 [1..3]

[出力]
foldr ( (+) 1 0 ) 0 (2:3:[])
foldr ( (+) ((+) 1 0) 2 ) 0 (3:[])
foldr ( (+) ((+) ((+) 1 0) 2) 3 ) 0 ([])
foldr ( (+) ((+) 1 2) 3 ) 0 ([])
foldr ( (+) 3 3 ) 0 ([])

--> 6

... Replies ...
これがあるのを思い出しました!
https://well-typed.com/blog/2017/09/visualize-cbn/
また私自身試してないもので恐縮ですが、実際のところHaskellのサブセットのようですが、十分目的は達成できるはずです。
@igrep
重ねてありがとうございます。
こちらも、調べてみます。
IOモナドの >>= について質問させてください

https://wiki.haskell.org/99_questions/21_to_28 の "Problem 23" を参考に
以下のようなコードを作成しました。
("abcdefgh" の中からランダムに 3 文字表示するプログラム)

[コード1]
import System.Random

rnd_select :: [a] -> Int -> IO [a]

rnd_select xs n = do
    let l = length xs - 1
    ys <- sequence . replicate n $ (randomRIO (0, l) :: IO Int)
    return $ map (xs !!) ys

これは想定通り動作しました。
ghci> rnd_select "abcdefgh" 3 >>= putStrLn
efc
it :: ()

この関数を少し変えて、`>>=` を使用するように変更しました。
[コード2]
rnd_select xs n = f >>= return . map (xs !!)
    where
        l = length xs - 1
        f = sequence . replicate n $ (randomRIO (0, l) :: IO Int)

この場合も問題ありません。

さらに、上記の f の部分を (where 句ではなく) 直接書いて以下のように変更しました。
[コード3]
rnd_select xs n = sequence . replicate n $ (randomRIO (0, l) :: IO Int) >>= return . map (xs !!)
    where
        l = length xs - 1

すると、以下のようにエラーとなります。
ghci> :l d
[1 of 2] Compiling Main             ( d.hs, interpreted )

d.hs:5:86: error: [GHC-83865]
    • Couldn't match type '[Int]' with 'Int'
      Expected: Int -> a
        Actual: [Int] -> [a]
    • In the second argument of '(.)', namely 'map (xs !!)'
      In the second argument of '(>>=)', namely 'return . map (xs !!)'
      In the second argument of '($)', namely
        '(randomRIO (0, l) :: IO Int) >>= return . map (xs !!)'
  |
5 | rnd_select xs n = sequence . replicate n $ (randomRIO (0, l) :: IO Int) >>= return . map (xs !!)
  |                                                                                      ^^^^^^^^^^^
Failed, no modules loaded.

メッセージから考えて [Int] ではなく Int が要求されているようだったので、以下のように変更したところ
問題なく動作しました。
[コード4]
rnd_select xs n = sequence . replicate n $ (randomRIO (0, l) :: IO Int) >>= return . (xs !!)
    where
        l = length xs - 1

説明が長くなってしまい恐縮ですが、`[コード3]` と [コード4] のような違いが出る理由が理解できませんでした。
... Replies ...
Haskell プログラムらしい書き方について質問させてください

import Control.Exception
import Control.Monad
import System.Directory
import System.Posix.Files
import System.Posix.Types

lessThan :: FileOffset -> FilePath -> IO Bool
lessThan threshold path = do
    s <- getFileStatus path
    return $ and [isRegularFile s, fileSize s < threshold]

smallFiles :: FileOffset -> FilePath -> IO (Either String [FilePath])
smallFiles threshold dir = do
    ei <- try (listDirectory dir)

    case ei of
        Left (e::SomeException) -> return $ Left (displayException e)

        Right contents -> do
            paths <- filterM (lessThan threshold) contents
            return $ Right paths

引数で指定したディレクトリから、閾値以下のサイズのファイル・パスを取得する関数を書きました。

ghci> smallFiles 100 "."
Right ["retry.hs",".gitignore","oop.hs"]
it :: Either String [FilePath]

想定通りに動作することを確認しました。

上記のコードでは、`try (listDirectory dir)` が Either a b を戻すため、それを case で判定して
また Left や Right でくるむような形になっています。

これをスマートに記述する方法はあるのでしょうか ?

(怒られないので、調子に乗って質問ばかりさせていただいていますが、これがマナー違反であればご指摘ください)
... Replies ...
読んでいて勉強になりました。どんどん質問して盛り上げて欲しいです :raised_hands:
ポイントフリーにより書かれたコードを、わかりやすいように展開するようなツールなどはありますか ?

以前 こちらで質問させていただき、ポイントフリー化するツールを教えていただきました

これの逆を行う様なツールは存在しますか ?
ghci> f = ((+1) .) . (+)
f :: Num c => c -> c -> c
ghci> f 1 2
4
it :: Num c => c

このとき、f が f a b = (+1) $ (+) a b のように展開されるようなイメージになります。
ghci> f a b = (+1) $ (+) a b
f :: Num a => a -> a -> a
ghci> f 1 2
4
it :: Num a => a

... Replies ...
Reminder: beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。 Haskell-Beginners ML や IRCの#haskell-beginners  や RedditのMonthly Hask Anythingのような位置づけを意図しています。 beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。 • それはくだらない質問だ → くだらない質問など無い • その質問は以前にもあった → 質問者はそんなこと知らない • Google検索せよ → 検索できないから質問している beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。 • : とは何のことですか。 • タプルとは何ですか。
関数従属性について質問させてください

MonadState の定義を見て | m -> s が気になりました。
class (Monad m) => MonadState s m | m -> s where
	get :: m s
	put :: s -> m ()

調べてみたところ、以下のように説明がありました。
"->" の左側にある型によって右側の型が一意に決定される
それ以外にも説明のあるところもあったのですが、納得したといえるほど理解できませんでした。

例)
read "123" :: Int

:: Int がないと "123" がなんの型かわからないよね。

と、いう程度に簡単に理解できるような例はありますか ?
(例えがわかりにくかったら無視してください)
... Replies ...
@ has joined the channel
曖昧な質問となってしまい申し訳ありませんが

https://zenn.dev/osio/articles/2022-advent-calendar
に、以下のようなものがありました。
import Control.Monad (join)
f = join
f (*) 5 --> 25 = 5 * 5

Hoogle で join を調べ以下のような定義ということがわかりました。
join              :: (Monad m) => m (m a) -> m a
join x            =  x >>= id

これらをまとめると、以下になると思います。
join (*) = (*) >>= id

しばらく考えて、なんとなく関数モナドかなと思い、定義に当てはめて
h >>= f = \w -> id ((*) w) w

ここに引数を与えて、最終的に
id ((*) 5) 5 ==> (*) 5 5

と、ここまできてようやく納得できました。

前置きが長くなりましたが、`(*)` が関数であるため関数モナドに決まってるでしょ。

と、言われてしまえばそれまでなのですが、`(*)
= id` のようなものを見て、それが関数モナド ((->) r) であると導き出すのは経験しかないのでしょうか ?
... Replies ...
@ has joined the channel
目的の関数などの定義を検索する方法について質問させてください。

いつも Hoogle を利用していますが、StateT の empty の定義を探すときには
https://hoogle.haskell.org/?hoogle=StateT

上記の一覧からたどり、以下のソースを表示し
https://hackage.haskell.org/package/transformers-0.6.1.1/docs/src/Control.Monad.Trans.State.Lazy.html#StateT

ここから、ブラウザの検索機能で empty をテキスト検索で探しています。

目的自体は達成できていますが、もう少し探しやすい方法があればと思って質問させていただきました。
... Replies ...