include - OCaml 中的多态仿函数(与 Include 命令相关)

标签 include ocaml functor

编辑:我用模块类型 A 和 B 替换了抽象示例,用一个更具体的示例使用组和环。

我在一个使用众所周知的代数结构的例子中提出了我的仿函数问题。为组定义签名:

module type Group = 
sig
    type t
    val neutral: t
    val add: t -> t -> t
    val opp: t -> t
end

并为包含许多有用操作的组定义签名,例如这里共轭:
module type Extended_Group =
sig
    include Group
    val conjugate: t -> t -> t
end

共轭的实现只取决于加法和相反,所以我不想为我定义的所有组明确地写它,所以我写了以下仿函数:
module F (G: Group) =
struct
    include G
    let conjugate x y = add (add x y) (opp x)
end

现在,假设您正在使用“扩展”组概念的其他结构,例如整数环:
module Z = 
struct
    (* Group *)
    type t = int
    let neutral = 0
    let add x y = x+y
    let opp x = -x
    (* Ring *)
    let m_neutral = 1
    let mult x y = x*y
end

由于环是群的粒子情况,我们可以对它应用仿函数 F:
module Extended_Z = F(Z)

不幸的是,为了将 F 应用到 Z,Ocaml 首先将 Z 的类型限制为 Group,然后再应用 F。
一切的行为就像我这样做一样:
module (Group_Z: Group) = Z
module Extended_Z = F(Group_Z)

因此,尽管 Z.mult 有意义,但 Extended_Z.mult 对编译器来说是未知的。我不想失去 Extended_Z 仍然是戒指的事实!当然,包含编写一个仿函数 F_Ring 的解决方案与 F 一样,但将 Ring 作为输入并不是一个令人满意的解决方案:我不想编写 F_Fields、F_VectorSpace 等。

这是我想要的:
  • 一种扩展模块签名的方法,以公开更多值。
    可以用另一种方式来做,限制模块的签名(使用我用于 Group_Z 的语法),但我找不到放大签名的方法。这里会是这样的:
    module (Ring_Extended_Z: Ring) = Extended_Z
    
  • 或者,一种定义多态仿函数的方法,即定义一个接受模块的仿函数 F
    签名“至少组”并输出签名模块“至少扩展组”。当然,
    这仅在存在 include 时才有意义,这就是为什么我坚信 OCaml 中不存在这样的功能。

  • 在网上找了几天的答案后,我认为正确的实现方法其实是以下一种:
    (灵感来自 Real World OCaml 的第 9 章)
    module F (G: Group) =
    struct
        let conjugate x y = ...
    end
    

    (与以前相同,但没有包含)
    然后在同一点执行所有包含:
    module Extended_Z =
    struct
        include Z
        include F(Z)
    end
    

    有人可以确认这是实现我想要的好方法,而且我的第一种方法不适合
    OCaml 的精神?更具体地说,有人可以确认选项 1. 和 2. 在 OCaml 中确实是不可能的吗?

    编辑:关于动态类型

    在我看来,这仍然是静态类型,因为如果我写
    module Extended_Z = F(Z: Ring)
    

    OCaml 仍然可以将其作为具有额外值“共轭”的签名环的模块静态键入。它需要模块级别的多态性,
    我理解不存在,但我认为它可以存在而不使类型系统动态化。

    最佳答案

    我在这个答案中分享了我发现的两个部分令人满意的解决方案来实现我想要的。

    第一个是我的问题末尾建议的那个,在代数结构的情况下可能是“好”的一个。

    module type Group = 
    sig
        type t
        val neutral: t
        val add: t -> t -> t
        val opp: t -> t
    end
    
    module type Extended_Group =
    sig
        include Group
        val conjugate: t -> t -> t
    end
    
    module F (G: Group) =
    struct
        let conjugate x y = G.add (G.add x y) (G.opp x)
    end
    
    module type Ring = 
    sig
        include Group
        val m_neutral: t
        val mult: t -> t -> t
    end
    
    module Z = 
    struct
        type t = int
        let neutral = 0
        let add x y = x+y
        let opp x = -x
        (* Ring *)
        let m_neutral = 1
        let mult x y = x*y
    end
    
    module Extended_Z = 
    struct
        include Z
        include F(Z)
    end
    

    在这种情况下,由 OCaml 推断的 Extended_Z 的签名“扩展”了签名 Ring,并且确实 Extended_Z.mult 在模块外部是可见的。

    第二种解决方案使用 Andreas Rossberg 和 Ivg 建议的部分来模拟模块级别的多态性。在代数结构的情况下它不太令人满意,但对于我的实际问题来说它是完美的。
    module type Group_or_More =
    sig
        include Group
        module type T
        module Aux: T
    end
    
    module F (G: Group_or_More) =
    struct
        include G
        let conjugate x y = add (add x y) (opp x)
    end
    
    
    module Z = 
    struct
        type t = int
        let neutral = 0
        let add x y = x + y
        let opp x = -x
        module type T = 
            sig
                val m_neutral: t
                val mult: t -> t -> t
            end
        module Aux = 
            struct
                let m_neutral = 1
                let mult x y = x*y
            end
    end
    
    
    module Extended_Z = F(Z) ;;
    

    在这里,签名 Group_or_More 准确地捕捉到了我对签名扩展另一个签名的想法。你把所有额外的东西都放在辅助模块 Aux 中。但是,为了能够提供尽可能多的辅助功能,您需要将此 Aux 模块的签名指定为您的组的一部分。

    对于代数结构,它不太令人满意,因为 Z 和 Extended_Z 是较弱意义上的环:乘法由 Z.Aux.mult 和 Extended_Z.Aux.mult 访问。

    关于include - OCaml 中的多态仿函数(与 Include 命令相关),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38100250/

    相关文章:

    c - 棘手的包含 C 中的情况

    algorithm - 一些 OCAML 问题

    functional-programming - 为什么 ocamlc 说我不匹配 {} 而我没有?

    ocaml - 如何在 OCaml 中将实现留到稍后执行

    c++ - 结合仿函数和 lambda

    php - 如何在已经建立的网站上实现 "Maintenance Mode"

    c - 从环境变量生成 #include 宏

    c++ - 运算符作为函数指针

    c++ - 使用 std::sort 时禁止仿函数(继承)?

    include - 如何在 Symfony2 中找到包含的路径?