isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum
Applicative
それが機能していることはわかりますが、 (または)のインスタンスがどこから来ているのかわかりませんFunctor
。
isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum
Applicative
それが機能していることはわかりますが、 (または)のインスタンスがどこから来ているのかわかりませんFunctor
。
これは、一般的なタイプの関数のApplicative
インスタンスです。((->) r)
1つの引数を複製してすべての関数に使用することにより、同じ最初の引数タイプの関数を1つの関数に結合します。(<$>)
は関数の合成であり、純粋はconst
であり、次のよう(<*>)
に変換されます。
s :: (r -> a -> b) -> (r -> a) -> r -> b
s f g x = f x (g x)
この関数は、おそらくSコンビネータとしてよく知られています。
((->) r)
ファンクターはモナドでもありReader
、共有引数は「環境」値です。例:
newtype Reader r a = Reader (r -> a)
ファンクションをポイントフリーにするためにこれを行うのは一般的ではないと思いますが、イディオムに慣れると、実際に明瞭さが向上する場合があります。たとえば、あなたが挙げた例は、「文字は文字または数字である」という意味として非常に簡単に読むことができます。
Control.Applicative
パッケージから無料で、静的矢印と呼ばれるもののインスタンスを入手できます(Conor McBride et al。による「ApplicativeProgrammingwithEffects」を参照) 。したがって、どのソースタイプも、あなたの場合Char
、他のタイプa
がタイプにマップされるApplicativeインスタンスを生成しますChar -> a
。
これらのいずれかを組み合わせる場合、たとえば関数f :: Char -> a -> b
を値に適用する場合、セマンティクスは、引数を両方にフィードする新しいx :: Char -> a
関数を作成することです。Char -> b
f
x
f <*> x = \c -> (f c) (x c)
したがって、あなたが指摘するように、これはあなたの例を以下と同等にします
isAlphaNum c = (isAlpha c) || (isNum c)
私の意見では、そのような努力は必ずしも必要ではなく、HaskellがApplicative(おそらく2レベルの言語のようなもの)に対してより良い構文サポートを持っていればもっと良く見えるでしょう。
リフト機能を使用しても同様の効果が得られることに注意してください。例:
import Data.Char
import Control.Applicative
isAlphaNum = liftA2 (||) isAlpha isNumber
または、適用可能なインスタンスの代わりに((->)r)のモナドインスタンスを使用します。
import Data.Char
import Control.Monad
isAlphaNum = liftM2 (||) isAlpha isNumber
[余談]
1つの引数を2つの中間関数に分配し、結果を2変数に分配する方法がわかったので、 2つの引数を1つの中間関数に分配し、結果を2変数に分配したいという何らかの関連するケースがあります。
import Data.Function
orFst = (||) `on` fst
-- orFst (True,3) (False, 7)
--> True
このパターンは、たとえばcompare
関数によく使用されます。