isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum
我可以看到它有效,但我不明白Applicative
(or Functor
) 的实例来自哪里。
isAlphaNum :: Char -> Bool
isAlphaNum = (||) <$> isAlpha <*> isNum
我可以看到它有效,但我不明白Applicative
(or Functor
) 的实例来自哪里。
这是常见类型Applicative
的 , 函数的实例。((->) r)
它通过复制单个参数以供所有函数使用,将具有相同第一个参数类型的函数组合成一个函数。(<$>)
is 函数组合,pure is 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 等人的“Applicative Programming with Effects”) 。因此,在您的情况下,任何源类型Char
都会产生一个 Applicative 实例,其中任何其他类型a
都映射到 type 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 对应用程序有更好的句法支持(可能像 2 级语言)会更好。
应该注意的是,您可以通过使用提升功能获得类似的效果,例如:
import Data.Char
import Control.Applicative
isAlphaNum = liftA2 (||) isAlpha isNumber
或者,使用 ((->) r) 的 monad 实例而不是应用实例:
import Data.Char
import Control.Monad
isAlphaNum = liftM2 (||) isAlpha isNumber
[题外话]
既然您知道如何将一个参数分配给两个中间函数并将结果分配给一个二元函数,那么您希望将两个参数分配给一个中间函数并将结果分配给一个二元函数,这是一种相关的情况:
import Data.Function
orFst = (||) `on` fst
-- orFst (True,3) (False, 7)
--> True
例如,这种模式经常用于该compare
功能。