Surprise list

Potentially surprising parts of the E language.

Current as of E 0.8.33o.

For-loop pattern failure is not an error
If the key or value pattern of a  loop does not match, then the loop simply skips that element of the collection. This can be useful, but is unlike all other non-explicit pattern match operations ( and parameter lists).

Examples
# E sample ? def things := [1, "two", 3] # value: [1, "two", 3] ? for x :int in things { >    println(x) > } # stdout: 1 #        3  #

Alternative
Move the pattern into a :

# E sample ? for thing in things { >    def x :int := thing >    println(x) > } # stdout: 1 #          # problem: &lt;ClassCastException: String doesn't coerce to an int&gt;

The current developer version also allows strict checking to be turned on with a pragma (experimental):

? pragma.enable("for-must-match") ? for x :int in things { >    println(x) > } #
 * 1) E sample
 * 1) stdout: 1
 * 1) problem: 

doesn't return x
The forward declaration expression,, does not return var but rather the Resolver for it.

Examples
# E sample ? def x # value: &lt;Resolver&gt; ? x # value: &lt;Promise&gt;

Rationale
If you want to pass a resolver as an argument, using it as an “out parameter”, this syntax is helpful:

x.hereIsAResolver(def y) ... use y

Alternative
If you want the actual promise, simply write.

Unresolved references do not necessarily behave as their future resolved identity
&mdash; messages sent before the reference is resolved may be reacted to however the current holder of the reference &ldquo;arrow-head&rdquo; chooses, which does not necessarily correspond to the reference to which the unresolved reference resolves. This has been discussed in an e-lang thread.

Rationale
This cannot be fixed without removing pipelining, eliminating one of the major benefits of the E reference model.

Examples
For now, see this lengthy example by MarkM.

Alternative
To avoid being vulnerable to this type of misbehavior, do not use a sameness test or Map key lookup in order to decide on the reliability of the response to a previously sent message. This might involve using a when-catch/whenResolved construct to wait until the reference is resolved.

Accumulator operator is lowest-precedence
As you can see in the following expansion, the operator following the “_” is always lowest-precedence (as it is the one effectively rewritten into an assignment form):

? e`pragma.enable("accumulator"); accum 0 while (a) { _ * b + c }` # value: e`null ...   #                            accum__1 := accum__1.multiply(b.add(c)) ...

Rationale
None known; this is probably an accident of the definition of the expansion of accumulator syntax.

Alternative
Avoid accumulator syntax when the accumulation cannot be expressed as a single call.

Shadowing function arguments
Rebinding a name within a block is an error, e.g.

def foo { def x := 2 def x := 3 }

("Failed: x already in scope")

However, rebinding an argument does not issue any warning:

def foo(x) { ... 	def x := 2 ... 	println(x) }

Rationale
The already-in-scope error is intended to catch accidentally using the same name twice, not prohibit rebinding. Generally, you can expect it to be supressed anywhere there is visible  syntax.

Single-letter uriGetters are a special case
It is not possible to refer to a single-letter uriGetter in a URI literal.

? def  := ["foo" => "bar"] # value: ["foo" => "bar"] ? interp.setExpand(true) ?  # expansion: file__uriGetter.get("t:foo") # value: 

Rationale
This is a feature intended for convenient support for Windows drive letter filenames.

Alternative
Use names longer than one letter.