4 ms·
Ah, point taken. Here is a valid version: (case-host :clj (deftype Foo [] JvmOnlyInterface (aMethod [blah blah] ...)) :cljs
by wooby 12y ago
Ah, point taken. Here is a valid version:
(case-host
:clj
(deftype Foo []
JvmOnlyInterface
(aMethod [blah blah] ...))
:cljs
(deftype Foo []))
(extend-type Foo
SomeCommonProtocol
(someMethod [blah] ...))
It is true there are countless examples. There are also countless counter-examples. And for the examples that are competitive in terseness, there is already cljx.
I personally don't "just want to hack it", especially when it comes to writing portable code. I agree that portability poses subtle new challenges to application design. I just prefer to meet those challenges in a way that is programmable, which syntactic extension is definitely not.
- brandonbloom 12y agoLet me rephrase "just hack it": Feature expressions are a low-level primitive for platform switching. If you have cross platform abstractions, consumers of your code shouldn't need to use feature expressions. However, they are very useful in the implementation of such abstractions. As for the programability complaint: I just don't see why it is important. If you are generating code, you can already add/remove content from the generated code. That is: you can always just write a "feature macro"! Feature expressions don't add any power you don't already have: Only affordances for humans. Unrelated thought: Haskell/GHC uses the C preprocessor to address this problem.
- wooby 12y agoI agree that Feature Expressions don't add power, because yes, they are semantically equivalent to C preprocessing and orthogonal to the idea of Lisp. What excites us most about Feature Macros is that they do add power. Here is an example of something we think is powerful from the proposal - a cross-platform macro: (case-host :cljs nil :clj (defmacro my-macro [name & body] (case-target :clj `(.println (System/-out) (str (do ~@body))) :cljs `(.log js/console (str (do ~@body))))))
- brandonbloom 12y ago1) I don't understand what this example is supposed to demonstrate. 2) By "power" I meant a formal notion of expressiveness: Neither feature expressions nor feature macros add anything to the language that you can't already express with existing constructs. Like I said: It's about affordances, not capability.
- michaniskin 12y agoThe example demonstrates how you can define a macro that runs its code in Clojure but can emit code to both Clojure and ClojureScript. How would you do this with feature expressions?
- lukev 12y ago#+clj (defmacro my-macro [] (cond (contains? *features* :clj) `(some-clojure-thing) (contains? *features* :cljs) `(some-cljs-thing))) If that was too verbose for you, you could of course write a `feature-case` function that looks almost identical to what you have in that example.
- wooby 12y agoThis was a great example, and I think you are absolutely right that it is the identical in function, if not in form, to mine. Interestingly, in the course of concocting a counter-counter example, I came up with this in an effort to show the two dynamic variables we stipulate - host and target - were absolutely required: #+clj (defmacro my-macro [] (cond (contains? *features* :clj) `(some-clojure-thing) (contains? *features* :cljs) `~(do (require 'some-ns) ((resolve 'some-ns/some-fn))))) My hope was to make the point that 'some-ns/some-fn would run in a context where (features :cljs) is true - as it is in the caller's (macro body) environment - which would result in the system attempting to run ClojureScript code, which would explode because the macro is Clojure. Then I realized that require runs load, and load is in the macro body. The load/require available in the environment is Clojure-specific! Thus, load/require can bind disj :cljs and conj :clj before reading and compiling some-ns. My counter-example failed, but did bring me a little closer to the truth - that we only need one dynamic variable, platform. Any platform that has the ability to load code also has an opportunity to bind platform and thereby inform macros of target. This simplifies the Feature Macro proposal by half and we are excited to amend it. Our updated cross-platform example becomes: (case-platform :cljs nil :clj (defmacro my-macro [] (condp = *platform* :clj `(some-clojure-thing) :cljs `(some-cljs-thing)))) Instead of host and target, we are down to just platform. We continue to see no need for a features set because of Clojure's platform-symbiosis.