你可以做这样的事情(免责声明:我没有检查相关类型类的法律,并且构造函数中存在异常的字符串Alternative
让我怀疑它是否合法)。Scala 的异构andThen
由fmap
; 它的同质andThen
/compose
被>>>
/ <<<
from所覆盖Category
;orElse
被<|>
;覆盖 lift
是runToMaybe
。
但是,如果没有像 Scala 中那样的深度编译器集成,模式不完整性警告将与此交互不良。Haskell 对这些东西只有模块级的编译指示,你不会想在任何你声明不穷尽的函数的模块中不加选择地关闭它们,否则你可能会得到令人讨厌的惊喜。根据您的用例,您可能会发现光学更惯用且问题更少;您可以通过 Template Haskell 为您生成样板。
(注意:我之所以这么称呼它Inexhaustive
是因为PartialFunction
用词不当,因为它暗示那Function
是全部。但是 Scala 没有终止或积极性检查器,所以编译器实际上无法谈论全部;所以你会遇到这种奇怪的情况,其中不是偏函数的函数只是“常规” Function
,而您应该可以将其称为“总Function
”。这里的问题不是部分或全部,这是一个更广泛的想法,而是模式匹配的穷举性。)
{-# LANGUAGE TypeApplications #-}
module Inexhaustive
( Inexhaustive, inexhaustive
, runToMaybe, isDefinedAt
) where
import Prelude hiding ((.), id)
import Control.Applicative
import Control.Exception
import Control.Category
import Data.Maybe
import System.IO.Unsafe (unsafePerformIO)
newtype Inexhaustive a b = Inexhaustive (a -> b)
inexhaustive :: (a -> b) -> Inexhaustive a b
inexhaustive = Inexhaustive
runToMaybe :: Inexhaustive a b -> a -> Maybe b
runToMaybe (Inexhaustive f) x =
let io = fmap Just $ evaluate $ f x
in unsafePerformIO $ catch @PatternMatchFail io (\_ -> return Nothing)
isDefinedAt :: Inexhaustive a b -> a -> Bool
isDefinedAt f = isJust . runToMaybe f
instance Functor (Inexhaustive z) where
fmap f (Inexhaustive g) = inexhaustive (f . g)
instance Applicative (Inexhaustive z) where
pure x = inexhaustive (const x)
(Inexhaustive zab) <*> (Inexhaustive za) = Inexhaustive (\z -> zab z $ za z)
instance Alternative (Inexhaustive z) where
empty = inexhaustive (\_ -> throw $ PatternMatchFail "inexhaustive empty")
f <|> g =
inexhaustive $ \x ->
case runToMaybe f x <|> runToMaybe g x of
Just y -> y
instance Category Inexhaustive where
id = inexhaustive id
(Inexhaustive f) . (Inexhaustive g) = Inexhaustive (f . g)