斯卡拉在哈斯克尔的部分功能

时间:2018-06-13 20:50:16

标签: scala haskell partial-functions

Scala非常支持部分函数,​​主要是因为在Scala中定义部分函数时,它还为它定义了isDefinedAt函数。此外,Scala还具有orElseandThen函数来处理部分函数。

Haskell通过简单地非详尽地定义函数来支持部分函数(尽管在Haskell社区中强烈建议不要使用它们)。但是要定义isDefinedAt函数,你必须使用某种异常处理,这是我无法弄清楚的。定义isDefinedAt函数后,可以使用它来定义orElseandThen函数已经存在(.)

简而言之,我想定义一个函数,

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

3 个答案:

答案 0 :(得分:12)

我建议像Scala一样,为部分函数使用单独的类型。

path1

答案 1 :(得分:0)

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

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

这不是特别有用。

applyisDefinedAt仅在Scala中真正链接到需要编译器支持的PartialFunction文字:

  

A PartialFunction's value receives an additional isDefinedAt member, which is derived from the pattern match in the function literal, with each case's body being replaced by true, and an added default (if none was given) that evaluates to false.

我相信你可以通过使用Template Haskell来模拟这个,但老实说,我认为使用Daniel Wagner所说的更像Haskell的方法更好。正如他所提到的,懒惰有助于。

如果确保runKleisli f x仅执行一次,它会更好;优化同时包含isDefinedAtrunKleisli的情况需要使用Common Subexpression Elimination,并且编译器在执行此操作时要谨慎,请参阅Under what circumstances could Common Subexpression Elimination affect the laziness of a Haskell program?

答案 2 :(得分:0)

你可以做这样的事情(免责声明:我没有检查相关类型类的法律,Alternative中异常的构造函数中存在一个字符串让我不知道是否合法)。 Scala 的异构 andThenfmap 覆盖;其同质的 andThen / compose>>><<< / Category 覆盖; orElse<|> 覆盖; 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)