8

我想扩展现有的“核心”模块之一,例如Core.Option

module Microsoft.FSharp.Core.Option

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

(我知道bind函数,但我认为filter在某些情况下选项的方法更方便)。

filter但不幸的是,如果没有明确打开命名空间,我就无法使用方法Microsoft.FSharp.Core

// Commenting following line will break the code!
open Microsoft.FSharp.Core

let v1 = Some 42
let v2 = v1 |> Option.filter (fun v -> v > 40)

printfn "v2 is: %A" v2

在大多数情况下,如果不打开适当的命名空间,我们就无法使用模块中的函数。F# 编译器自动“打开”一些预定义(核心)命名空间(如Microsoft.FSharp.Core),这不会将“模块扩展”中的方法引入范围,我们仍然应该手动打开核心命名空间。

我的问题是:有什么解决方法吗?

或者扩展“核心”模块的最好方法是在自定义命名空间中创建这样的扩展并手动打开这个命名空间?

// Lets custom Option module in our custom namespace
module CustomNamespace.Option

    let filter predicate op = ...

// On the client side lets open our custom namespace.
// After that we can use both Option modules simultaneously!
open CustomNamespace

let v1 = Some 42
let b = 
    v1 |> Option.filter (fun v -> v > 40) // using CustomNamespace.Option
    |> Option.isSome // using Microsoft.FSharp.Core.Option
4

4 回答 4

7

对于生产代码,我会按照 Taha 的回答建议:创建您自己的模块并根据需要打开/为其设置别名。作为程序员,你一生的大部分时间都花在阅读代码上。在不清楚方法来自何处的 F# 代码中,阅读 F# 代码可能会非常令人沮丧。

话虽如此,我惊讶地发现这有效:

namespace Microsoft.FSharp.Core

module Option =

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

namespace USERCODE

module Option = Microsoft.FSharp.Core.Option

module M =

    let test () =
        Some 1
        |> Option.filter (fun x -> x > 0)
        |> Option.map (fun x -> x + 1)

它并没有消除在文件头部写入内容的需要,但它确实解决了需要打开命名空间的问题。不相关,Microsoft.FSharp.Core因为默认情况下它总是打开的,但对其他命名空间很有帮助。

于 2013-04-18T12:05:30.783 回答
5

如果将 AutoOpen 属性添加到模块有帮助吗?

[<AutoOpen>]
module Microsoft.FSharp.Core.Option

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

编辑

这有效,但只能跨越程序集边界。它在同一个程序集中不起作用:

namespace Microsoft.FSharp.Core
module Option =
    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

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

要从另一个程序集中调用它:

[<EntryPoint>]
let main args = 
    let f () = Some "" |> Option.filter (fun f -> true)
    Console.WriteLine("Hello world!")
    0
于 2013-04-18T09:46:46.980 回答
5

为了扩展 F# 模块,请创建另一个具有相同名称的模块:

module Option =

    let filter predicate op =
        match op with
        | Some v  -> match predicate v with true -> Some v | false -> None
        | None    -> None


let v1 = Some 42
let v2 = v1 |> Option.filter (fun v -> v > 40)

printfn "v2 is: %A" v2
于 2013-04-18T09:03:09.407 回答
0

根据文档,这样的东西应该可以扩展类型。但是我一定有什么问题,因为它不会编译并给出。以下错误:

此类型扩展的一个或多个声明的类型参数缺少或错误的类型约束与 'Option<_>'F# Compiler (957) 上的原始类型约束不匹配

open System.Runtime.CompilerServices

[<Extension>]
type Option with
    [<Extension>]
    static member fromBool predicate word =
        if predicate word
        then Some word
        else None

这不是一个有效的答案,而是原始问题的附录。不确定是否值得打开一个新线程?

于 2019-12-04T20:18:45.827 回答