【问题标题】:Clojure - dispatch on return type? (As expressive as Haskell Typeclasses)Clojure - 按返回类型分派? (与 Haskell 类型类一样富有表现力)
【发布时间】:2014-03-30 04:17:04
【问题描述】:

这是一个关于 Clojure 与 Haskell 等其他语言的表达能力的问题。更广泛的问题是Expression Problem的解决方案

This question 得出的结论是,通常 Clojure 协议(和多方法)的表达能力不如 Haskell 类型类,因为协议在第一个参数上分派,而 Haskell 类型类可以在返回类型上分派。 (现在我觉得这个推理真的很有趣,对发动语言战争没有兴趣。我只对思路清晰感兴趣)。

作为打破这种推理的一部分 - 我的问题是 - 我们不能创建一个按返回类型调度的 Clojure 多方法(或类型提示)。我认为我们可以将以下表达式放入 Clojure 多方法中:

(= java.lang.String (:tag (meta #'my-string)))

函数在哪里:

(defn ^String my-string [] 
  "hello world")

编辑: 关键是我可以跑:

(meta #'my-string)

并在没有函数评估的情况下得到以下结果:

{:arglists ([]), :ns #<Namespace push-price.core>, :name my-string, :column 1, 
:line 1, :file "/private/var/folders/0l/x6hr0t1j2hvcmm_sqq04vdym0000gn/T/form-
init7576840885484540032.clj", :tag java.lang.String}

即我有一些关于我的函数的预期类型的​​信息,但没有对其进行评估。


编辑 3(2014 年 4 月 24 日):

假设我有以下类型: (deftype string-type [])

(deftype int-type [])

然后我根据这些类型定义了以下函数:

(defn #^{:return-type string-type} return-string [] 
  "I am returning a string")

(defn #^{:return-type int-type} return-int [] 
  42)

现在我编写一个函数来调度它们的返回类型,如下所示:

(defn return-type-dispatch [fn-arg]
  (let [return-type (:return-type (meta fn-arg))]
    (cond 
     (= return-type string-type) 
     "This function has a return type of the string type"
     (= return-type int-type) 
     "This function has a return type of the integer type"
     :else (str "This function has a return type of:" return-type))))

然后我编写一个宏在编译时运行它

(defmacro compile-time-type-check-string []
  (println (return-type-dispatch #'return-string)))

(compile-time-type-check-string)

然后我像这样测试它:

lein uberjar

这给出了以下结果:

$ lein uberjar
Compiling dispatch-type-compile-time.core
This function has a return type of:class dispatch_type_compile_time.core.string-type
...

所以我似乎是在按返回类型进行调度。

【问题讨论】:

    标签: haskell clojure typeclass multimethod clojure-protocol


    【解决方案1】:

    让我们想象一下 Clojure 中存在返回类型多态性。这将允许我们编写类似 Haskell 类的东西

    class Default a where def :: a
    

    在 Haskell 中这是可行的,因为像这样的片段是编译时错误

    def
    

    众所周知,在编译时这意味着什么是模棱两可的。类似地,如果我们要编写 Clojure 片段

    (def)
    

    不可能知道如何将该调用分派给正确的实例。更清楚地说,Clojure 的评估顺序是片段中的顺序

    (foo x y z)
    

    表达式xyzfoo 之前得到评估。为了使(def) 工作,它需要以某种方式检查foo(并因此强制其评估),以获取有关如何使用(def) 的返回值的信息。

    这可以在 CPS 转换之后完成,在这种情况下,(def) 将被转换为类似(在 Haskell 类型表示法中)的函数

    class Default a where def :: (a -> r) -> r
    

    现在我们看到我们可以检查延续函数以了解有关它期望的参数类型的信息,然后将其发送出去。

    最后,只要有足够的宏魔法,就可以做到这一点……但现在可能比在 Clojure 上实现 Haskell 风格的类型系统更省力。类型化的 Clojure 可能是一个很好的模型,除非它被显式设计,因此 Clojure 的语义不会受到推断类型的影响。这正是在返回类型多态中发生的,因此在 Typed Clojure 中显然是不可能的。

    【讨论】:

    • “为了让 (def) 工作,它需要以某种方式检查 foo (并因此强制其评估)”但是 (meta 上面的方法没有明确解决这个问题吗?你可以获得 return未经评估的信息?
    • 但是(def) 的评估是如何评估其调用上下文的meta 的?这就是返回类型多态所必需的。
    • 我添加了一个在正确返回类型上调度的示例。你能修改你的答案吗?
    • 您所展示的内容无法比较。在技​​术术语中,您发送的是返回“类”而不是返回“类型”,尽管这些术语很容易产生误导。关键的属性是考虑静态而不是仅在运行时可以知道的内容。
    • 太好了 - 感谢您的反馈。我已将其更新为使用返回类型而不是返回类。我还演示了在编译时运行的检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 2012-01-09
    • 2018-11-27
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 2018-10-10
    相关资源
    最近更新 更多