[英]Scala's Partial Functions in Haskell
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 偏函數的一篇不錯的文章是 - 如何在 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 }
這不是特別有用。
apply
和isDefinedAt
僅在 Scala 中真正鏈接到需要編譯器支持的PartialFunction
文字:
我相信,您可以通過使用 Template Haskell 來模擬這一點,但老實說,我認為使用 Daniel Wagner 的回答中描述的更像 Haskell 的方法更好。 正如他所提到的,懶惰有幫助。
如果您確保runKleisli fx
只執行一次,它會更好; 優化同時擁有isDefinedAt
和runKleisli
需要公共子表達式消除,編譯器對此持謹慎態度,請參閱在什么情況下公共子表達式消除會影響 Haskell 程序的惰性?
你可以做這樣的事情(免責聲明:我沒有檢查相關類型類的法律,並且在構造函數中存在一個字符串以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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.