haskell-jp / beginners #23

@HN has joined the channel
@まど has joined the channel
@ai-ou has joined the channel
@yoshitsugu has joined the channel
@yoshitsugu has joined the channel
@mocamocaland has joined the channel
@h r has joined the channel
@Greg Fragin has joined the channel
@フラワー has joined the channel
@ has joined the channel
@kudos3939 has joined the channel
@西澤秀樹 has joined the channel
@Hisawo TAKASE has joined the channel
Reminder: beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。 Haskell-Beginners ML や IRCの#haskell-beginners  や RedditのMonthly Hask Anythingのような位置づけを意図しています。 beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。 • それはくだらない質問だ → くだらない質問など無い • その質問は以前にもあった → 質問者はそんなこと知らない • Google検索せよ → 検索できないから質問している beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。 • : とは何のことですか。 • タプルとは何ですか。
こんばんは。学習のために色々な処理をfunctionalなアプローチで書いてみる中で浮かんだ疑問です。

手続き的な書き方は難しくないのに、functionalに書こうと思うと急に難度があがる処理があるように感じ始めています。
当たり前のことなのですが、これは慣れるしかないのですよね……?
自分には到底書けないよと思う処理も、関数型言語に慣れ親しむうちに処理が書けるようになっていくのでしょうか?:smiling_face_with_tear:

例えば`List.GroupBy`をあえて自分で実装するとなると、
手続き型では「forループを回して、毎要素ペアを条件判定し、適宜新しい配列を作成したり既存の配列に追加したり」で済むところが、
関数型では下記のように(*私にとっては*)難しい書き方になってしまいます。
myGroupBy :: (a -> a -> Bool) -> [a] -> [[a]]
myGroupBy p xs = foldl step [] xs
    where step ys x | null ys = (x:[]):ys
                    | p (head (last ys)) x = (init ys) ++ (((last ys) ++ (x:[])):[])
                    | otherwise = ys ++ ((x:[]):[])

自力では書けなかったので、 を引用しています(正直、このコードにバグがあるかどうかも私には自信を持っては分かりません)。
このような処理を書けるようになるためには、他言語の習得と同様に練習するしかないのですよね……!

すみません、とても当たり前のことしか言っていないのですが、難しさに圧倒されて思わず何かご意見を伺いたくなってしまい……:eyes:
... Replies ...
詳しい方教えてください。

引数の値を、複数の関数(Char -> Bool) に適用して or をとることを考えています。
最初にシンプルに考えて以下のようになりました。
import Data.Char
f x = or $ map ($ x) [isDigit, isLetter]

ghci> f '1'
True

これをポイントフリー化しようと以下のように変更し
f x = or . flip map [isDigit, isLetter] $ ($ x)

ghci> f '1'
True

ここまでは良かったのですが、最後に引数を消すことを考え

f = or . flip map [isDigit, isLetter] . ($)

ghci> :t f
f :: ((Char -> Bool) -> Bool) -> Bool

このようにしたところ、これは期待している型と違ってしまいます。

うまく説明できているかわかりませんが、上記のような場合
ポイントフリーな書き方は可能でしょうか ?
... Replies ...
@光志賀 has joined the channel
関数型 (function type) の意味がなんとなく判ってくると自然にそういう発想になるかなと思います。
「σ と τ が型ならばσ → τ も型である」つまり、関数はそれ自身で値をもつ一級の(計算)対象で、なにも特別なものではないということが腑におちるとよいかもしれません。ちょっと手前味噌ですが、2019年のHaskell dayのときの「お話」のスライドです。御笑覧ください。
https://github.com/nobsun/hday2019/blob/master/doc/ftype.pdf
@ has joined the channel
@Keonhwa Ryu has joined the channel
(>>=) と (>=>) の違いについて

ChatGPT で教えてもらったところ
(>>=)はバインド演算子と呼ばれ、モナド値を取り、モナドの中の値を抽出し、それを次の関数に渡します。
一方、(>=>)は関数合成演算子であり、モナドを扱う関数を合成するための演算子です。
との事でした。

簡単に試してみたところ、以下のようなコードになったのですが
違いは最初の関数 (f) の引数の渡し方くらいでした。

この二つの違いは、そのくらいの認識で問題ないでしょうか ?
import Control.Monad

f x = Just $ x + 1
g x = if x > 0 then Just (x * 2) else Nothing
h x = Just $ "The number is " ++ show x

fgh    = f   >=> g >=> h
fgh' x = f x >>= g >>= h

main = do
  putStrLn $ show $ fgh  1
  putStrLn $ show $ fgh' 1

... Replies ...
@ has joined the channel
@EtoAl has joined the channel
@Archgen has joined the channel
分岐処理について教えて下さい

Maybe を使った場合
import Control.Monad

f :: String -> Maybe String
f x = do
    guard (x == "MZ")

    return "SUCCESS"


main :: IO ()
main = do
    print $ f "MZ"
    print $ f "ELF"

このような形 (guard) で条件によって処理を中断ができます。
しかし、それ以外の文脈の場合この方法は使えないと思います。

具体的には以下のようなプログラムを作っています。
import 
import System.Directory ( getCurrentDirectory )
import System.FilePath ( joinPath )

import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Internal as BI ( w2c )
import Data.Binary.Get

getC :: Get Char
getC = BI.w2c <$> getWord8

readPE :: Get (Maybe String)
readPE = do
    isMZ <- (== "MZ") <$> sequence [getC, getC]

    if isMZ then
        return $ Just "SUCCESS"
    else
        return Nothing

main :: IO ()
main = do
    xpath <- joinPath . flip (:) ["src", "a.exe"] <$> getCurrentDirectory

    withBinaryFile xpath ReadMode $ \h -> do
        dat <- B.hGetContents h
        print $ runGet readPE dat

    print "done."

上記の readPE 関数の中で if による分岐を行っていますが
もし、この後も条件による分岐が必要だった場合 if 文をネスト
させていくような形で作るのが正しいのでしょうか ?
... Replies ...
@tedzuka has joined the channel
型の異なる値の計算について教えて下さい

以下の関数 f は a, b に対し fromIntegral を適用することで (+) の引数とできます。
import Data.Word
import Data.Function ( on )
import Control.Monad ( liftM2 )

f = do
    let a = pure 1 :: Maybe Word32
    let b = pure 2 :: Maybe Word64

    let a' = fromIntegral <$> a
    let b' = fromIntegral <$> b

    let c = (+) <$> a' <*> b'

    print c
---
ghci> f
Just 3
it :: ()

これを on 関数を使って一気に適用すると a, b の型が異なるため不可能です。
g = do
    let a = pure 1 :: Maybe Word32
    let b = pure 2 :: Maybe Word64

    let c = on (liftM2 (+)) (fromIntegral <$>) a b

    print c
---
a.hs:23:50: error:
    • Couldn't match type 'Word64' with 'Word32'
      Expected: Maybe Word32
        Actual: Maybe Word64
    • In the fourth argument of 'on', namely 'b'
      In the expression: on (liftM2 (+)) (fromIntegral <$>) a b
      In an equation for 'c': c = on (liftM2 (+)) (fromIntegral <$>) a b
   |
23 |     let c = on (liftM2 (+)) (fromIntegral <$>) a b
   |   

このような場合、なにか上手に解決する方法はあるのでしょうか ?
... Replies ...
@bbmallow has joined the channel
@ has joined the channel
@Lefl has joined the channel
@Sakae has joined the channel
@ has joined the channel
@aja has joined the channel
@goma11 has joined the channel
@MaxBruchDev has joined the channel
bunny_hopper isolated
@bunny_hopper isolated has joined the channel
Reminder: beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。 Haskell-Beginners ML や IRCの#haskell-beginners  や RedditのMonthly Hask Anythingのような位置づけを意図しています。 beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。 • それはくだらない質問だ → くだらない質問など無い • その質問は以前にもあった → 質問者はそんなこと知らない • Google検索せよ → 検索できないから質問している beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。 • : とは何のことですか。 • タプルとは何ですか。
@Shogo has joined the channel
@ has joined the channel
@ has joined the channel
@ has joined the channel
@ has joined the channel
@zackey has joined the channel
Masaaki Saitow
@Masaaki Saitow has joined the channel
@milran has joined the channel
@りょー has joined the channel
@ has joined the channel
@ryoppippi has joined the channel
Reminder: beginnersチャンネルは、新しい人がスムーズにHaskellに慣れるための質問を歓迎するチャンネルです。 Haskell-Beginners ML や IRCの#haskell-beginners  や RedditのMonthly Hask Anythingのような位置づけを意図しています。 beginnersチャンネルでの回答側は、以下の左側のような応答を厳禁とする運用です。 • それはくだらない質問だ → くだらない質問など無い • その質問は以前にもあった → 質問者はそんなこと知らない • Google検索せよ → 検索できないから質問している beginnersチャンネルでは、例えば以下のレベルの質問から歓迎します。 • : とは何のことですか。 • タプルとは何ですか。