6

Scala 对偏函数有很好的支持,主要是因为在 Scala 中,当你定义一个偏函数时,它也isDefinedAt为它定义了一个函数。Scala 也有orElseandThen函数一起使用部分函数。

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 的 Partial Functions 的一篇不错的文章是 - How to create and use partial functions in Scala By Alvin Alexander

4

3 回答 3

14

我建议像在 Scala 中一样,为偏函数使用单独的类型。

import Control.Arrow
import Data.Maybe

type Partial = Kleisli Maybe

isDefinedAt :: Partial a b -> a -> Bool
isDefinedAt f x = isJust $ runKleisli f x
-- laziness should save some of the work, if possible

orElse :: Partial a b -> Partial a b -> Partial a b
orElse = (<+>)

andThen :: Partial a b -> Partial b c -> Partial a c
andThen = (>>>)
于 2018-06-13T21:02:31.143 回答
0

您的版本isDefinedAt不是 Scala 所做的(即使在签名中);当为真时,a 很可能(尽管不鼓励)PartialFunction抛出异常。isDefinedAt或者,当您明确定义一个(不使用文字)时,反之亦然:apply当为假时不必抛出isDefinedAt,用户有责任不调用它。所以直接的等价物就是

data PartialFunction a b = PartialFunction { apply :: a -> b, isDefinedAt :: a -> Boolean }

这不是特别有用。

apply并且isDefinedAt仅在 Scala 中真正链接PartialFunction需要编译器支持的文字:

PartialFunction 的值接收一个额外的 isDefinedAt 成员,该成员派生自函数文字中的模式匹配,每个案例的主体被替换为 true,以及一个附加的默认值(如果没有给出),其计算结果为 false。

我相信,您可以使用 Template Haskell 来模拟这一点,但老实说,我认为使用 Daniel Wagner 的回答中描述的更像 Haskell 的方法更好。正如他所提到的,懒惰是有帮助的。

虽然如果你确保runKleisli f x只执行一次它会更好;优化同时拥有isDefinedAtrunKleisli需要公共子表达式消除的情况,并且编译器对此非常谨慎,请参阅在什么情况下公共子表达式消除会影响 Haskell 程序的惰性?

于 2018-06-14T09:10:51.783 回答
0

你可以做这样的事情(免责声明:我没有检查相关类型类的法律,并且构造函数中存在异常的字符串Alternative让我怀疑它是否合法)。Scala 的异构andThenfmap; 它的同质andThen/compose>>>/ <<<from所覆盖CategoryorElse<|>;覆盖 liftrunToMaybe

但是,如果没有像 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)

于 2021-07-23T16:20:32.140 回答