haskell-jp / beginners #23 at 2023-04-13 11:31:21 +0900

詳しい方教えてください。

引数の値を、複数の関数(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

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

うまく説明できているかわかりませんが、上記のような場合
ポイントフリーな書き方は可能でしょうか ?
こういうときに便利なサービスがありまして、 というやつです。
結果はこちららしいです:
f = or . flip map [isDigit, isLetter] . flip id

あまり読みやすいとは思えませんが!
(途中で間違って投稿してしまいました!すみません! Data.Monoid.Any を使った解法は改めて見てそれほど良いと思えなかったので消しました)
<@U4LGTMTMK>
回答ありがとうございました。
便利そうで、今後 多用します
m(_ _)m
元の投稿で言うと \x ->〈式〉 $ ($ x)〈式〉 . ($) が等価じゃなくて、 〈式〉 . flip ($) にしてやる必要がありますね
ポイントフリー化の基本は
h x = g (f x)   ⇔   h = g . f

なので、構文的にやるなら、変数をひたすら右へ追いやる感じで変形します
f x = or (map (flip ($) x) [isDigit, isLetter])
f x = or (flip map [isDigit, isLetter] (flip ($) x))
f   = or . flip map [isDigit, isLetter] . flip ($)

意味的にやるなら、関数を合成して関数をつくることになるので、述語の選言を構成する二項演算子を定義しておいて、それで畳み込みをすればよさそうです.
(<||>) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(p <||> q) x = p x || q x
f = foldr (<||>) (const False) [isDigit, isLetter]

<||> のポイントフリーまでやりたければ、Sコンビネータを使います.
(<||>) p q x = (||) (p x) (q x)
(<||>) p q   = (<*>) ((||) . p) q
(<||>) p     = (<*>) ((.) (||) p)
(<||>)       = (<*>) . ((||) .)

難読化になってしまいますね.
@nobsun
教えてもらうと const False が単位元? になって各引数を適用する foldr に
なるのだな、と思えるのですが
そういった発想になるように上達するような書籍なり、サイトなり
良い情報源はありますか ?
関数型 (function type) の意味がなんとなく判ってくると自然にそういう発想になるかなと思います。
「σ と τ が型ならばσ → τ も型である」つまり、関数はそれ自身で値をもつ一級の(計算)対象で、なにも特別なものではないということが腑におちるとよいかもしれません。ちょっと手前味噌ですが、2019年のHaskell dayのときの「お話」のスライドです。御笑覧ください。
https://github.com/nobsun/hday2019/blob/master/doc/ftype.pdf
また、Haskell ではないんですが、SICP にPainter(手続き)を対象とする図形言語を構成する例があります。
「本節では, データ抽象と閉包の能力を示す, 絵を描くための単純な言語を紹介し, また高階手続きを本質的に利用する. 」
@nobsun
丁寧にありがとうございました。
腑に落ちる。というところまでは時間がかかると思いますが教えてもらった資料を参考に、勉強を続けます。