gpt4 book ai didi

.net - F# 多种类型的通用中缀运算符(fmap、applicative、bind 等)

转载 作者:行者123 更新时间:2023-12-04 23:14:45 25 4
gpt4 key购买 nike

我想做的是使用中缀 fmap(我定义为 <^>)来处理多种类型,例如 Option 和 Either(自定义类型)。

鉴于:

   type Either<'a, 'b> = Left of 'a | Right of 'b 

在代码中我希望能够做到:
   let fO (a : int option) = None
let fE (a : Either<string,int>) = Left "dummy"

let mO = Some 1
let mE = Right 1

let testO = f0 <^> m0
let testE = fE <^> mE

其中 (<^>) 为每个:
    let (<^>) f m = match m with | Some a -> Some <| f a | None -> None
let (<^>) f m = match m with | Right a -> Right <| f a | Left a -> Left a

为了让 Option <^> 工作,我扩展了模块:
    namespace Microsoft.FSharp.Core
[<AutoOpen>]
module Option =
let (<^>) f m = match m with | Some a -> Some <| f a | None -> None

[<assembly:AutoOpen("Microsoft.FSharp.Core")>]
do ()

对于任何一个:
    type Either<'a, 'b> = Left of 'a | Right of 'b with
static member (<^>) (f,m) = match m with | Right a -> Right <| f a | Left a -> Left a

这几乎可以工作,但是一次只能使用一个。
一个 Either 模块也可以附加到 FSharp.Core,但同样你只能拥有一个或另一个。

我知道这可以通过 2 种自定义类型来完成,比如 Either 和 Maybe(Haskell 选项),但是我想坚持使用 Option。

欢迎任何和所有建议。

最佳答案

这在 F# 中并不容易表示,唯一的方法是使用静态解析的类型参数,并且通常不被认为是惯用的。

对于新的自定义类型,这样做非常容易,但将其改造成现有类型则更为复杂。再次支持两者都稍微困难一些。

您可以继续使用的方法是使用为现有类型硬编码的静态方法创建单案例区分联合的辅助类型:

type Functor = Functor
with
static member FMap (Functor, mapper : 'T -> 'U, opt : Option<'T>) : Option<'U> =
Option.map mapper opt
static member FMap (Functor, mapper : 'T -> 'U, ch : Choice<'T, _>) : Choice<'U, _> =
match ch with
|Choice1Of2 v -> Choice1Of2 (mapper v)
|Choice2Of2 v -> Choice2Of2 v

现在您可以使用具有静态解析类型参数的函数来根据类型选择适当的方法:
let inline fmap (f : ^c -> ^d ) (x : ^a) =
((^b or ^a) : (static member FMap : ^b * ( ^c -> ^d ) * ^a -> ^e ) (Functor, f, x))

注意 ^b or ^a健康)状况?这也为我们提供了一种将这种行为插入自定义类型的方法。
type Either<'a, 'b> = Left of 'a | Right of 'b with
static member FMap (Functor, f, m) =
match m with | Right a -> Right <| f a | Left a -> Left a

对于运算符形式,只需定义:
let inline (<^>) f x = fmap f x

你最终定义了函数:
val inline fmap :
f:( ^c -> ^d) -> x: ^a -> ^e
when (Functor or ^a) : (static member FMap : Functor * ( ^c -> ^d) * ^a -> ^e)
val inline ( <^> ) :
f:( ^a -> ^b) -> x: ^c -> ^d
when (Functor or ^c) : (static member FMap : Functor * ( ^a -> ^b) * ^c -> ^d)

现在你可以用 <^> 来做这种事情。运算符(operator):
let x  = (fun x -> x + 1) <^> (Some 1)
let x' = (fun x -> x + 1) <^> (None)
let z<'a> : Either<'a, _> = (fun x -> x + 2) <^> (Right 2)
let z' = (fun x -> x + 2) <^> (Left 5)

你也可以看看 F#+为了更完整地实现许多这些标准功能抽象。

关于.net - F# 多种类型的通用中缀运算符(fmap、applicative、bind 等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45731559/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com