簡體   English   中英

(如何)你能 curry 組成一元函數嗎?

[英](How) Can you curry compose monadic functions?

我有以下功能:

f: a -> m[b]
g: (b,c) -> m[d]
h: (a,c) -> m[d]

h如何表示為fg的組合?

使用do/for表示法,我們可以像這樣輕松實現h

h: (a,c) => {
 for {
  b <- f(a)
  d <- g(b,c)
 } yield (d)
}

但是,我很好奇我們是否可以這樣表達: h = f andThen g其中andThen被用作單子組合運算符。 例如:

f: a -> m[b]
g: b -> m[c]
h: a -> m[c] = f andThen g

我假設在 Haskell (例如,Kliesli >=> )等語言中可以創建這樣的andThen function 。 在 Scala 中,我們可以這樣寫:(在 Scala 命名中的示例andThenE因為andThen已經在Function1的實例上定義)。

implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
    def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
      (v1: A) => e.apply(v1).flatMap(b => f.apply(b))
    }
}

鑒於此,如果我們對函數進行 curry,我們似乎可以實現這樣的組合(或者至少看起來是可能的):

f: a -> m[b]
g: b -> c -> m[d]
h: a -> c -> m[d] = f andThen g

理論上這可以工作,但我不知道這是否可能或如何在 Scala (或 Haskell,盡管我更熟悉前者)中實現類似的東西。

假設我們有以下功能:

case class Error(e:String)
case class Output(i: Int, f: Float, s: String)
case class IntermediateOutput(i:Int, f:Float)

def f(i:Int): Either[Error, IntermediateOutput] = Right(IntermediateOutput(i+1, i*0.33)
def g(io: IntermediateOutput, s: String): Either[Error, Output] = Right(Output(io.i, io.f, "hello "+s)) 

val h: (Int, String) => Either[Error, Output] = f andThen g

val result = h(1, "world!") //Right(Output(2, 0.33, "hello world!")

這甚至可能/可以實現嗎? 如果不是 Scala,我們怎么能 go 關於curry 在 Haskell 或一般情況下組成一元函數

這是已知的事情,還是我們明確區分適用於非單子函數的柯里化和為單子函數保留andThen like 運算符,但避免將兩者混合? 如果是這樣,我可以看到do/for符號的有力案例。 但是,我並不完全相信這是不可能的,並且想進一步了解這一點。 也許代碼會很混亂,沒關系 - 我只是好奇。 由於處理現有問題,我偶然發現了這種情況,但我不能這樣投。

在 Haskell 中有一些標准(即在base庫中)運算符。

首先,您的andThen function 是眾所周知的Kleisli 組合

>=> :: (a -> m b) -> (b -> m c) -> a -> m c

        a -> m b
               b -> m c
       -----------------
        a        -> m c

由於g在元組中操作並且f不返回元組,因此此運算符與您的類型不完全匹配。 這可以通過do/for符號輕松克服

h :: Monad m => (a -> m b) -> ( (b,c) -> m d ) -> (a,c) -> m d
h f g (a, c) = do
  b <- f a
  g (b, c)

對於上面的解決方案,我會使用 go,但為了好奇,這個問題已經遇到過,Haskell 的base庫引入了一個面向類別理論的模塊,稱為Control.Arrow 在這里,您可以找到大量運算符來實現您的目標:

import Control.Arrow

hKleisli :: Monad m => (a -> m b) -> ( (b,c) -> m d ) -> (a,c) -> m d
hKleisli f g = runKleisli $ 
  first (Kleisli f) >>> Kleisli g
--|                 |   |- this is just boilerplate
--|                 |- This composes Categories
--|- this converts f into a function operating in tuples

{--
       Kleisli f  :: Kleisli m  a     b         --  a    -> m  b
---------------------------------------------
first (Kleisli f) :: Kleisli m (a,c) (b,c)      -- (a,c) -> m (b,c)
        Kleisli g :: Kleisli m       (b,c) d    --            (b,c) -> m d
---------------------------------------------
first (Kleisli f)  
    >>> Kleisli g :: Kleisli m (a,c)       d    -- (a,c)            -> m d
--}

編輯

關於您的評論:最初的問題是:我們如何在柯里化g后組成fg 而且我的解決方案看起來更像是讓我們 uncurry fg一起工作,所以我同意這不是一個完整的解決方案。 好的,讓我們解決您的問題,但首先,一些注意事項:

  • 從類型h:: a -> c -> md應該很清楚,我們想要一些行為類似於m但將c納入考慮范圍的 monad。
  • f:: a -> mb的類型我們知道f無法訪問c並且應該以某種方式將它帶入 scope。 否則, fh永遠不可能是同一個單子。
  • 坦率地說,我們可以使用const. f:: a -> c -> mb const. f:: a -> c -> mb

到目前為止,我們有

{-- 
The name of the type variables are chosen to match the ones used in this post, but are different in ghci

        f :: a      -> m b
        g :: (b,c)  -> m d

const . f :: a -> c -> m b
  curry g :: b -> c -> m d
--}

現在很明顯,我們需要使用一些帶有const. f const. fcurry g但問題是我們需要保留 monad m並且除非我們將結果包裝成一些新的數據類型,否則無法實現,否則,我們將引用的 monad 是 function monad (->) (這是 haskell 特定的嗎?我認為不是)。 顯而易見的選擇是使用Kleisli monad ( ghc >= 8.10 )。 所以現在我們有:

{-- 
The name of the type variables are chosen to match the ones used in this post, but are different in ghci

        f :: a      -> m b
        g :: (b,c)  -> m d

const . f :: a -> c -> m b
curry   g :: b -> c -> m d
                 |- This result lives in the -> monad

Kleisli . const . f :: a -> Kleisli m c b
Kleisli . curry   g :: b -> Kleisli m c b
--}

import Control.Monad
import Control.Arrow

f :: Monad m => a -> m b
f = undefined

g :: Monad m => (b, c) -> m d
g = undefined

-- And now, We have curryed and composed g
h :: Monad m => a -> c -> m b
h = runKleisli . (f' >=> g')
  where
    f' :: Monad m => a -> Kleisli m c b
    f' = Kleisli . const . f
    g' :: Monad m => b -> Kleisli m c d
    g' = Kleisli . curry g

請注意,這可以使用與Kleisli不同的 monad 來完成。 可能所有解決方案都是同構的 curry / uncurry。 只要您可以將c帶到f的 scope 並找到一個保留m行為的 monad,您就可以應用它。

暫無
暫無

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

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