
Not Explicit - kawera
https://boats.gitlab.io/blog/post/2017-12-27-things-explicit-is-not/
======
millstone
I didn't agree with this article's definition of "explicit," which feels
tautological:

 _Rust is explicit because you can figure out a lot about your program from
the source of it._

 _But as long as the source code conveys the information, it is still explicit
in the narrow sense I defined above._

 _But if the thing will happen deterministically in a manner that can be
derived from the source, it is still explicit in the narrow sense that I laid
out earlier._

Source code conveys everything possible about your program in any language.

The best notion of "explicit" is captured by the critique of "local." Here's
some nice ways in which Rust is more "local" than C++:

1\. Macros require bang!s instead of C++ surprise-insides

2\. References require &s and `mut &`s instead of C++ implicit references.

3\. Int -> FP conversions require a typecast

In every case, both Rust and C++ allow you to discover the runtime behavior by
reading the source code. But I would argue that Rust is more explicit than C++
in every case, wouldn't you agree?

 _A feature of Rust that is not implicit, but also not local, is method
resolution_

Isn't method resolution intimately tied to Deref coercion, which allows a type
to _implicitly_ (Rust's language) implement methods of another type? So method
resolution seems like one of the more implicit features within Rust.

 _but all of it is explicit if you look at the impl blocks for Vec <T>._

Well, all of Ruby's monkey-patching is explicit in the sense that you can find
it if you know where to look.

It's a good effort to refine the meaning but "local" IMO is the best one!

~~~
__s
Your example comparing to C++ is something where I think the point of this
article is coming out: we can become more explicit about what we're talking
about when we bring nuance into our language to describe things like 'local'
vs 'statically determined' which in language design discussions are both being
labelled 'explicit' despite being distinct concepts. Explicit has been
overused to the point of being a bad word

~~~
plumenator
As in "explicit content"?

------
gravypod
What bothers me about languages like Rust, which I think I'm in the minority
on, is the lack of explicit type declarations.

C and Java, my two first languages, strictly spell out what types they expect.
Sure you _can_ ignore those rules but as long as you don't those types convey
a lot of useful information about your program. With languages like Rust I
don't think I could ever start writing code in them without an IDE. The entire
idea of compiler-specified types is neat but I don't think all of your code
should rely on that.

One oddity is Python which I have used a lot. The only difference is that
Python is much less strict than the languages I'm used to. Because of this
learning Python was still relatively difficult (Why is all the documentation
written like a book!? I miss my Doxygen) but I didn't need an IDE.

~~~
kibwen
_> is the lack of explicit type declarations_

This is mistaken. Rust has explicit type declarations, augmented with type
inference. Furthermore, unlike popular statically-typed functional languages,
Rust's type inference is deliberately restricted to only operate within
functions (intraprocedural) rather than between functions (interprocedural),
which means that one always has explicit types available in the function
signature when reading code.

~~~
curun1r
Perhaps not type declarations, but I think GP is onto something when it comes
to Rust and how it handles types in a way that often lacks explicitness.
There's a lot of Rust code where you have to have familiarity with an API to
understand what's going on with the types.

As a for instance, let's say you have a function:

    
    
        fn connect(addr: std::net::IpAddr) -> ... {
            ...
        }
    

And it gets called:

    
    
        connect("192.168.0.1".parse());
    

Unless you have a pretty good level of familiarity with the APIs involved,
it's pretty difficult to reason about the types when looking at just the
invocation of connect. You'd have to know that str::parse() will figure out
what type it's supposed to be and, if it implements FromStr, it will call the
from_str() function defined for that type to handle the coercion. That's a
frustrating level of indirection for a beginner. And, since all of this is
defined in the std library, it's also possible for library authors to
similarly declare APIs that have this kind of indirect invocation where you
only really understand what's going on if you've studied/memorized that API.
I've often thought that a maze of From/Into and Deref coercions would be an
excellent approach to the underhanded Rust competition because it's often so
difficult to realize when that code is getting executed and it would be pretty
easy to hide malicious code in functions that are executed implicitly.

It also doesn't help that a sizable portion of the Rust community believes
console-based editors like vi and emacs are preferred and more advanced Rust
IDEs are still early days and aren't able to help out much with situations
like these.

It's not that I disagree with these types of language design decisions. I just
feel this kind of lack of explicitness makes the language optimized for
experienced users rather than beginners and helps give Rust it's deserved
reputation for having a high learning curve.

------
awfullyjohn
Not sure what he was going for with that definition of "explicit." It wasn't
explicit at all.

> you can figure out a lot about your program from the source of it.

How does "implicit" not satisfy this condition? I can tell a lot about a
program from the source of it implicitly as well.

------
scribu
What a useful breakdown!

It seems like the narrow definition of “explicit” that the author uses is
equivalent to “deterministic”. If that’s true, then “explicit” could always be
replaced with a more precise alternative.

~~~
bad_user
No, I don’t think that’s what the author is saying, but to make it clear the
definition is:

 _“In computer science, a deterministic algorithm is an algorithm which, given
a particular input, will always produce the same output, with the underlying
machine always passing through the same sequence of states”_

In turn what the author is saying is precisely what he says, no more, no less
- explicit code is code whose behavior you can understand just by reading it,
with no external context, like how the runtime currently works on a particular
hardware.

The examples he gives are very clear as well. For example he claims to know
the stack allocations that will happen just by looking at the definitions of
the data types. You can’t know this on the JVM for example because the JVM
does optimizations under the hood that the programmer cannot control or reason
about. This doesn’t yield non-determinism. What happens is that the developer
ends up programming for a higher level machine that hides some details of the
underlying hardware.

Explicit isn’t always better than implicit, an argument that the author also
makes.

Overall I find the article very compelling and I’ll be sure to use it for
reference, as it’s what I also think, but with better words.

~~~
judofyr
> In turn what the author is saying is precisely what he says, no more, no
> less - _explicit code is code whose behavior you can understand just by
> reading it_ , with no external context, like how the runtime currently works
> on a particular hardware.

I'm not sure where this definition is coming from. Let's look at this code:

    
    
        vec.len()
    

I understand this behaviour very clearly. This will invoke the method given
the following rules:

\- Look for `len` on the type of `vec`

\- If `vec` implements Deref it will look for `len` there

\- Look for `len` on traits that the type of `vec` implements

\- And so on…

Yes, the behaviour is conditional, but it's still well-defined and easy to
understand, although complex.

"Implicit" is usually used when you want to describe situations where there
are multiple interpretations, and one is chosen due to context. In Ruby:

    
    
        first_name + " " + last_name
    

In this example `first_name` can either be a local variable or a method call
(which can either be in the same class, an included module, or a
method_missing). Here it turns out it was a method call, and we therefore call
this an "implicit method call". I can rewrite:

    
    
        self.first_name + " " + self.last_name
    

In this case there is a single interpretation: It can only be a method call.

However, while this code is _explicit_ on whether or not it's a method call,
it's not explicit about _which_ method is being invoked. Usually this
implicitness is considered a good feature since it allows abstraction,
polymorphism and so on.

------
octothorpes
It is worrying that you have to explicitly/verbosely write article about
explicitness, I thought people knew.

~~~
wgjordan
To the contrary, the article about _implicitness_ is the one that doesn't need
to be written because people already know what it means.

