(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
}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...
(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 {})))By the way, gen-nextvalue is a protocol function.
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.
idiomatic == I am doing what I am told to do without understanding why it sometimes makes sense, and sometimes doesn't, and sometimes just doesn't matter.
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)
)
)If you're going to do a lot of appending, might I suggest a ListBuffer or a Vector?
TraversableLike, and FilterMonadic will both size up the resulting return value based upon the length of what's passed to it. You'll get a List of N items, and the call to flatMap will also traverse the list to remove/flatten the Options. Items will then get dropped in the List. You're looking at one or two collectable objects every time you run through that method, as opposed to bin.items.length collectable objects in your original code.
Look at it this way-- what happens if bin.items is a List() of 10M objects? completed_items can be replaced up to 10M times. The .flatMap creates one collection of 10M length, removes the None objects, and returns the resulting List.
def complete_items(completed_binindex: Int, bin: ItemBin, nonterminal: Nonterminal, value: Value): MoreItems = {
for {
(item, v) <- bin.items
_ <- expectedSymbol(item)
nextValue <- grammar.nextValue(document, item.origin, bin.binindex, completed_binindex,
nonterminal,item.ruleindex, item.dot, v, value)
} yield {
item.inc -> nextvalue
}Programming in Clojure I miss the following features from Scala most:
a) Pattern matching
b) Having machine-checked documentation (aka type system), traits.
c) A proper documentation generation tool. The Clojure one doesn't do protocols properly, although protocols are the only real way in Clojure to have something akin' to interfaces and Scala traits.
a) https://github.com/clojure/core.match
b1) https://github.com/clojure/core.typed
b2) I agree that Clojure's facilities for mixins is under-utilized and that it lacks a proper facility for delegation (ie implicit conversions in Scala), but see (doc extend) for how awesome "traits as data" can be. Here's an example: https://github.com/stuartsierra/clojure.walk2/blob/2250e04c7...
c1) I hate generated documentation, but I understand why it's necessary for Scala. When I did some scala programming, I was so pleased with ScalaDoc, since it really helps you navigate that complex scala.collections hierarchy. Feels like having a pretty decent substitute to a good IDE. In Clojure, I don't feel the need nearly as much... Also, I quite like reflective documentation with the doc macro.
c2) Already covered interfaces/traits in b2