mirror of
https://github.com/carp-lang/Carp.git
synced 2024-11-12 12:49:13 +03:00
d420635762
* feat: overwrite existing interface implementations This commit alters the behavior of interfaces so that implementations with the same type signature will overwrite previous implementations with that signature--before this was a runtime error. Previously, if a user defined two distinctly named implementations of an interface that shared a type, Carp would panic and error at runtime if the interface was called and resolved to the type, since it couldn't decide which implementation to use from the type alone. After this commit, we instead issue a warning and overwrite existing implementations of the same type, so that defining: ``` (defn foo [] 0) (implements zero foo) ``` will replace `Int.zero` in the `zero` interface's implementation path list and won't result in a runtime error--instead `foo` will be called when `zero` is called in a context in which it returns an int: ``` [WARNING] An implementation of the interface zero with type (Fn [] Int) already exists: Int.zero. It will be replaced by the implementation: foo. This may break a bunch of upstream code! ``` test/interface.carp also has a concrete illustration of this case. * chore: address hlint suggestions * fix: don't print overridden interface implementations in info This commit updates our handling of interface overrides to remove interfaces from the implements meta of a function that was overridden by a new implementation. Similarly, this refactors primitiveInfo to prevent printing binders that do not actually implement an interface. * refactor: incorporate @TimDeve's error message suggestion
63 lines
1.7 KiB
Plaintext
63 lines
1.7 KiB
Plaintext
;; Test Interfaces
|
|
|
|
(load "Test.carp")
|
|
(use Test)
|
|
|
|
(definterface foo (Fn [a] Int))
|
|
|
|
;; A module implements an interface using implements.
|
|
;; Implementations don't need to share names with interfaces.
|
|
(defmodule A
|
|
(defn bar [x] x)
|
|
(implements foo A.bar))
|
|
|
|
;; Implementations may be declared before definitions
|
|
;; like `doc`, the name is relative to the module environment
|
|
(defmodule B
|
|
(implements foo baz)
|
|
(defn baz [y] (if y 5 0)))
|
|
|
|
;; Interfaces may be implemented retroactively
|
|
;; global functions can also implement interfaces.
|
|
(sig gojira (Fn [&String] String))
|
|
(defn gojira [s] @s)
|
|
(implements monster gojira)
|
|
|
|
(definterface monster (Fn [a] String))
|
|
|
|
;; An interface name can be used as a default implementation
|
|
(defn monster [scary?] (if scary? @"RAWR" @"meow"))
|
|
(implements monster monster)
|
|
|
|
;; If multiple implementations of the same concrete type are provided,
|
|
;; one overwrites the other.
|
|
(defn laugh-monster [times] (String.repeat times "LOL"))
|
|
(implements monster laugh-monster)
|
|
(defn pikachu [times] (String.repeat times "pika"))
|
|
(implements monster pikachu)
|
|
|
|
(deftest test
|
|
(assert-equal test
|
|
&2
|
|
&(foo 2) ;; A.foo
|
|
"Implements works as expected.")
|
|
(assert-equal test
|
|
&5
|
|
&(foo true) ;; B.foo
|
|
"Implementations can be declared before definitions.")
|
|
(assert-equal test
|
|
"SKRYEEE"
|
|
&(monster "SKRYEEE")
|
|
"Interfaces can be implemented retroactively.")
|
|
(assert-equal test
|
|
"meow"
|
|
&(monster false)
|
|
"Implementations may be global, and an implementation with the same name may
|
|
be used as a default.")
|
|
(assert-equal test
|
|
"pikapikapika"
|
|
&(monster 3)
|
|
"Implementations may be overwritten, when multiple implementations of the same type
|
|
are provided.")
|
|
)
|