3 ms·
I can show you two code samples of mine, one in Clojure, the other one doing something very similar in Scala: (defn completion-items [grammar doc items-bin-i
by auggierose 13y ago
I can show you two code samples of mine, one in Clojure, the other one doing something very similar in Scala:
(defn completion-items [grammar doc items-bin-index completed-bin-index items nonterminal value]
(loop [items items
completed '()]
(if-let [[^Item item previous-value] (first items)]
(let [alpha (expected-symbol grammar item)
dot (.dot item)]
(if (and alpha (= nonterminal alpha))
(let [rule-nonterminal (.nonterminal item)
ruleindex (.ruleindex item)
origin (.origin item)
value' (gen-nextvalue grammar doc origin items-bin-index completed-bin-index
rule-nonterminal ruleindex dot
previous-value value)]
(if (nil? value')
(recur (next items) completed)
(let [item' (->Item rule-nonterminal ruleindex (+ dot 1) origin)]
(recur (next items) (cons-item-value completed item' value')))))
(recur (next items) completed)))
(apply hash-map completed))))
--------------------------------------------------------
def complete_items(completed_binindex : Int, bin : ItemBin, nonterminal : Nonterminal, value : Value) : MoreItems = {
var completed_items : List[(Item, IntermediateValue)] = List()
for ((item, v) <- bin.items) {
val s = expectedSymbol(item)
if (s == Some (nonterminal)) {
grammar.nextValue(document, item.origin, bin.binindex, completed_binindex, nonterminal,
item.ruleindex, item.dot, v, value) match
{
case None =>
case Some(nextvalue) =>
completed_items = (item.inc -> nextvalue) ::
completed_items
}
}
}
completed_items
}
- brandonbloom 13y agoThat's quite non-idiomatic Clojure... 1) Unnecessary type hint 2) No use of destructuring or field lookup by keyword 3) Alteration of record objects with positional constructors instead of update-in 4) Explicit loop with previous item instead of reduce over (partition 2 1 items) 5) Way too many parameters 6) Apply hash-map instead of into 7) (+ ... 1) instead of inc 8) Weird gen-nextvalue auxiliary function instead of a lazy sequence I could keep going...
- TylerE 13y agoI don't suppose I could talk you into re-writing it into idiomatic clojure? Since someone else already rewrote it in idiomatic scala.
- brandonbloom 13y agoJust guessing from context, but probably something like this: (defn completion-items [items options] (->> (for [[item v] items] (when-let [nonterminal (expected-symbol item options)] (when-let [next-value (gen-next-value v options)] [(update-in item [:dot] inc) next-value]))) (remove nil?) (into {})))
- auggierose 13y agoThe thing is: In order to have performance, you need type hinting (a lot of it). I DO need performance, as the algorithm above is already part of something O(n^3). There also clojure.core.type does not help. And that is the problem with a language feature which is not a compiler feature as well. By the way, gen-nextvalue is a protocol function.
- brandonbloom 13y ago> In order to have performance, you need type hinting (a lot of it). No, not really. That has only been my experience when interoperating with Java libraries. Protocol dispatch has inline call caches, which are almost as fast as normal java interface dispatch. > that is the problem with a language feature which is not a compiler feature as well You could also say "that is the problem with type system features which are not virtual machine features as well". There is lots of erased information in the Scala type system that could be used for optimizations, but the VM can't make much use of that. Scala's type system does 3 things: 1) Enable type-related optimizations 2) Ensure internal consistency (ie prevent errors) 3) Enhance expressivity through logic solver search Clojure's type hints provide just enough type system to trigger host optimizations, where as core.typed is for finding and preventing bugs. I consider the implicitness of #3 to be an anti-feature.
- 13y ago
- mark242 13y agoHere's a more functional version of complete_items, if you're interested. def complete_items(...) = bin.items.flatMap((item, v) => expectedSymbol(item).map(nonterminal => grammar.nextValue(document, item.origin, bin.binindex, completed_binindex, nonterminal, item.ruleindex, item.dot, v, value) .map(nextvalue => (item.inc -> nextvalue) ) )
- auggierose 13y agoWhat can I say, I like my loops :-) http://arxiv.org/abs/1007.3023 http://arxiv.org/abs/1007.3023
- mark242 13y agoLoops obviously have their place, no question, but you're going to find that assigning a List to a var, and then appending to it / re-assigning it is going to generate a ton of garbage, and in general will just be slow all around. There's a reason that prepending/appending to Lists is "strange" in Scala. This is why map and flatMap are so great, because they take care of a lot of that heavy lifting for you (eg the matcher on None/Some -- this is what map is made for!).
- auggierose 13y agoThere is no performance difference here, map etc. are producing the same garbage. Scala isn't Haskell. Also note that there is a HUGE performance difference between appending and prepeding to a list. :-)
- bad_user 13y agoIf you need to append, use a Vector instead.
- GyrosOfWar 13y ago> Also note that there is a HUGE performance difference between appending and prepeding to a list. :-) If you're going to do a lot of appending, might I suggest a ListBuffer or a Vector?