Quoting and macro leakage, take 2

57 views
Skip to first unread message

Dave Thomas

unread,
Jul 6, 2014, 9:00:29 PM7/6/14
to elixir-lang-core

I’m trying not to be picky, but I just bumped into another case where macros
leak out to the user level.

I wanted to pass a function into a structure definition, so I wrote

defmodule Earmark.Context.Options do
  defstruct  do_smartypants: &(&1)  # default is a noop
end

and I got

** (CompileError) lib/earmark/context.ex: invalid quoted expression: #Function<0.54589463 in file:lib/earmark/context.ex>

Eric Meadows-Jönsson

unread,
Jul 6, 2014, 9:50:00 PM7/6/14
to elixir-l...@googlegroups.com
This happens, as you probably know, because the fields are evaluated at compilation time and the function wont survive after compilation. If we instead evaluated the fields at a later point we would not be able to implement struct deriving. Because to implement a protocol for a struct all fields have to be known.

It is not clear to me what the issue here. Is it that you are not allowed to put functions as default values or that the error message is bad?


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Eric Meadows-Jönsson

Dave Thomas

unread,
Jul 7, 2014, 12:16:19 AM7/7/14
to elixir-lang-core
On Sun, Jul 6, 2014 at 8:49 PM, Eric Meadows-Jönsson <eric.meado...@gmail.com> wrote:
This happens, as you probably know, because the fields are evaluated at compilation time and the function wont survive after compilation. If we instead evaluated the fields at a later point we would not be able to implement struct deriving. Because to implement a protocol for a struct all fields have to be known.

It is not clear to me what the issue here. Is it that you are not allowed to put functions as default values or that the error message is bad?

It's another example of something that really looks like it should work that doesn't. Someone coming to Elixir from somewhere else will be confused as to why. 

Lest I be thought of as just being picky, let me explain why I report these.

I want to see Elixir succeed and grow. To help in this, I report things that I see to be barriers.

Many of the issues I report are direct consequences of implementation decisions—decisions that were made for the best of reasons. However, when these decisions result in an uneven user experience (map values can't be used the same way list values are; function values can't be used the same way as other values), and then obscure errors (invalid quoted expression), I feel that you're risking putting people off. It makes the language feel poorly thought out.

I know the opposite is true. 

But, from the perspective of someone looking for a clean, nice to use language, each of these things feels like one more reason to look elsewhere for their next language.

So I guess my answer to your final question is "yes to both".


Dave

Peter Hamilton

unread,
Jul 7, 2014, 12:50:55 AM7/7/14
to elixir-l...@googlegroups.com

Are things too familiar? Are we in the "uncanny valley", where the small imperfections are magnified?

Compare the Palm Pilot and the Apple Newton. The Newton claimed that its handwriting recognition was good enough to just use. It was 99% accurate, but the last 1% drove everyone nuts. The Palm Pilot on the other hand introduced Graffiti, a special way of writing each character. With Graffiti, accuracy was impeccable. By requiring a small change in behavior from its users (learn Graffiti) the Palm Pilot was able to deliver a superior experience.

I wonder if Elixir is having the same problems here. It feels like we can use Elixir just like we might with Ruby or Python. That 1% of functionality that is unintuitive is painful.

Is there something we can require of users that will help? Rather than try to get Elixir to match naive user expectations, is there a way that we can manage those expectations? For example, rather than hide macros, can we make them more obvious and a first class part of the user experience of using Elixir? (Yes, they are first class I. The language, but they are sort of swept under the rug in the overall initial experience)

--

Gilbert Kennen

unread,
Jul 7, 2014, 2:38:51 AM7/7/14
to elixir-l...@googlegroups.com
Here is another direction to come from.

When people are learning a language, they form a mental construct of how
things work based on the various experiences they have with it. This is
a cycle of experiences which expand, reinforce, and refine their
preconceptions. The worst experience is when someone has an entrenched
concept of how things work and that concept suddenly, and inexplicably
fails.

In the current example, the problem is that people are told from a very
early stage that functions act like any other sort of data, but due to
implementation details which are likely non-obvious to users of likely
sophistication to try and use functions as default values in structs.

Here are various ways to avoid this sort of experience including some
benefits and drawbacks to each:

* Redesign features so that they behave according to a more consistent
conceptual framework either by 'fixing' the problem or providing clear
language structures which indicate when the problem will occur. This can
be difficult to accomplsh cleanly with good performance, but it also
tends to result in fewer special solutions, which means more explicit,
elegant code.

* Improve error messages so that users who experience the problem are
directed to documentation which explains the situation and suggested
solutions. Once again, it can sometimes be difficult to create
performant error messages for some classes of problems and even if you
do, it can cause frustration for those who encounter them, but when
implemented well, users build trust in the documentation and consistent
patterns are reinforced in the community.

* Manage expectations early by providing more fully nuanced explanations
of language features. Unfortunately, reducing complexity is very
important for increasing the likelihood that somebody will maintain
interest in learning a language and the greater the list of exceptional
circumstances, the greater the complexity. Also, the more details which
need to be understood earlier, the lower the learner's retention rate,
and thus the greater the number of people who may have read about the
exception, but when they get around to seeing it, have a bad experience.
The only benefit is that the language features need not be adjusted.

Dave is in the business of helping people to develop a cognitive
framework and when he points out issues like this it means he is having
difficulty keeping that framework simple, but is worried that by not
covering these cases, people are going to run into them without the
knowledge needed to recover from them gracefully.

Details like this may seem like obscure edge cases, but they send
signals to evaluators that there are hidden pitfalls just under the
surface. People looking to build solid infrastructure don't like
uncertainty and surprises and even if these surprises are rare, they sow
the seeds of doubt and that has tremendous capacity for keeping projects
on the fringes.

José Valim

unread,
Jul 7, 2014, 4:03:45 AM7/7/14
to elixir-l...@googlegroups.com
Poor error messages are completely unacceptable. If you are working on anything and you see an error message that does not make clear what happened and how to solve it, please file a bug report. Because that's what they are, bugs.

Now I consider this particular issue to be different to the unquote one. In the unquote one, you were manipulating quoted expressions, so you need to know what is a valid quoted expression. It is like trying to remove characters from two strings with -- and being surprised strings are not supported in that operator.

This case, however, is completely the opposite. defstruct uses a particular implementation and that error is leaking the implementation detail all over the place. Unfortunately there are constraints that does not allow us to have anonymous functions as a struct field but we could at least have a proper error message detailing why a function is not valid and which kind of functions are.

I have opened an issue to revisit the places where the "invalid quoted expression" may appear here: https://github.com/elixir-lang/elixir/issues/2499


José Valim
Skype: jv.ptec
Founder and Lead Developer


--

Saša Jurić

unread,
Jul 7, 2014, 9:50:41 AM7/7/14
to elixir-l...@googlegroups.com


On Monday, July 7, 2014 3:50:00 AM UTC+2, Eric Meadows-Jönsson wrote:
This happens, as you probably know, because the fields are evaluated at compilation time and the function wont survive after compilation. If we instead evaluated the fields at a later point we would not be able to implement struct deriving. Because to implement a protocol for a struct all fields have to be known.


I'm confused here a bit. For struct deriving, do you need to know only field names, or you need their values as well? Because if it's the former, then you could treat values as ASTs and Dave's example could work, right?

Eric Meadows-Jönsson

unread,
Jul 7, 2014, 9:52:45 AM7/7/14
to elixir-l...@googlegroups.com

We need to evaluate everything immediately to get the fields, otherwise defstruct Module.func() wouldnt work.



--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Eric Meadows-Jönsson
Reply all
Reply to author
Forward
0 new messages