簡體   English   中英

Scala 在 Haskell 中的偏函數

[英]Scala's Partial Functions in Haskell

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 偏函數的一篇不錯的文章是 - 如何在 Scala 中創建和使用偏函數 作者:Alvin Alexander

我建議,就像在 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 = (>>>)

您的isDefinedAt版本不是 Scala 所做的(即使在簽名中); 這是非常有可能的(雖然灰心)的PartialFunction拋出一個異常時, isDefinedAt是真實的。 或者,當您顯式定義一個(不使用文字)時,反之亦然:當isDefinedAt為 false 時, apply不必拋出,用戶有責任不調用它。 所以直接等價物就是

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

這不是特別有用。

applyisDefinedAt僅在 Scala 中真正鏈接到需要編譯器支持的PartialFunction文字:

PartialFunction 的值接收一個額外的 isDefinedAt 成員,它派生自函數文字中的模式匹配,每個 case 的主體被替換為 true,以及一個附加的默認值(如果沒有給出),其計算結果為 false。

我相信,您可以通過使用 Template Haskell 來模擬這一點,但老實說,我認為使用 Daniel Wagner 的回答中描述的更像 Haskell 的方法更好。 正如他所提到的,懶惰有幫助。

如果您確保runKleisli fx只執行一次,它會更好; 優化同時擁有isDefinedAtrunKleisli需要公共子表達式消除,編譯器對此持謹慎態度,請參閱在什么情況下公共子表達式消除會影響 Haskell 程序的惰性?

你可以做這樣的事情(免責聲明:我沒有檢查相關類型類的法律,並且在構造函數中存在一個字符串以Alternative的異常讓我想知道它是否合法)。 Scala的異構andThen被覆蓋fmap ; 它的同質andThen / compose>>> / <<< from 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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM