Scala非常支持部分函数,主要是因为在Scala中定义部分函数时,它还为它定义了isDefinedAt
函数。此外,Scala还具有orElse
和andThen
函数来处理部分函数。
Haskell通过简单地非详尽地定义函数来支持部分函数(尽管在Haskell社区中强烈建议不要使用它们)。但是要定义isDefinedAt
函数,你必须使用某种异常处理,这是我无法弄清楚的。定义isDefinedAt
函数后,可以使用它来定义orElse
,andThen
函数已经存在(.)
。
简而言之,我想定义一个函数,
isDefinedAt :: (a -> b) -> a -> Bool
isDefinedAt f x = -- returns True if f is defined at x else False
任何人都可以告诉我如何编写这样的功能。
注意,我可以定义一个带签名的函数
isDefinedAt :: (a -> b) -> a -> IO Bool
代表通用b
。但我想在共域中没有IO的功能。
关于Scala的部分函数的一篇很好的文章是 - How to create and use partial functions in Scala By Alvin Alexander
答案 0 :(得分:12)
我建议像Scala一样,为部分函数使用单独的类型。
path1
答案 1 :(得分:0)
您的isDefinedAt
版本不是Scala所做的(即使是签名);当PartialFunction
为真时,isDefinedAt
很可能(尽管不鼓励)抛出异常。或者,当您明确定义一个(不使用文字)时,反之亦然:apply
在isDefinedAt
为假时不必抛出,用户不应该再调用它。所以直接等价物只是
data PartialFunction a b = PartialFunction { apply :: a -> b, isDefinedAt :: a -> Boolean }
这不是特别有用。
apply
和isDefinedAt
仅在Scala中真正链接到需要编译器支持的PartialFunction
文字:
我相信你可以通过使用Template Haskell来模拟这个,但老实说,我认为使用Daniel Wagner所说的更像Haskell的方法更好。正如他所提到的,懒惰有助于。
如果确保runKleisli f x
仅执行一次,它会更好;优化同时包含isDefinedAt
和runKleisli
的情况需要使用Common Subexpression Elimination,并且编译器在执行此操作时要谨慎,请参阅Under what circumstances could Common Subexpression Elimination affect the laziness of a Haskell program?
答案 2 :(得分:0)
你可以做这样的事情(免责声明:我没有检查相关类型类的法律,Alternative
中异常的构造函数中存在一个字符串让我不知道是否合法)。 Scala 的异构 andThen
被 fmap
覆盖;其同质的 andThen
/ compose
被 >>>
的 <<<
/ 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)