Improve bit syntax #421

Closed
josevalim opened this Issue Jul 30, 2012 · 30 comments

Comments

Projects
None yet
6 participants
Owner

josevalim commented Jul 30, 2012

A good candidate is:

<< foo | size(1) - utf8 - binary - unit(8) >>
Owner

josevalim commented Aug 2, 2012

I have been thinking more about this. Today, iterating a binary is painful, we need to do:

def printable?(<<h, t | :binary>>) when some_condition do

def printable?(<<h, t | :binary>>) when other_condition do

Since Elixir promotes the use of binaries. This is something we should be concerned and make easier. It would be nice if instead we allowed a syntax similar to lists:

def printable?(<<h | t>>) when some_condition do

Which is much better and familiar. This is something I would like to play with, the only issue is that today we already use | for customizing the binary, so we would need to introduce this feature in two steps:

  1. v0.7 would introduce a new syntax and deprecate the one using |
  2. v0.8 would introduce <<h | t>>

So the only big question is: what should the new bit syntax look like? Some proposals:

  1. Use // (or one of ^, ~) as the separator:

    << foo // size(1) - utf8 - binary - unit(8) >>
    << foo ^ size(1) - utf8 - binary - unit(8) >>
    << foo ~ size(1) - utf8 - binary - unit(8) >>
    
  2. Use some keyword as separator, may be as:

    << foo as binary >>
    

    as looks weird though in case you want to specify unit or size:

      << foo as size(1) >>
    
  3. Use / as separator as erlang:

    << foo / size(1) - utf8 - binary - unit(8) >>
    

    I don't like this one because / is a valid operator and I don't want to introduce the same issue as erlang, where the left side requires parenthesis in order to be any expression.

Any other ideas in mind?

/cc @yrashk

Contributor

krestenkrab commented Aug 2, 2012

Have you considered how to specify typespecs (for functions) inline? I don't remember what you've done here, but it imho would make sense to use :: as in Haskell for a type specification.

fn( a :: integer, b :: binary) :: integer -> ...

Point being, that it would make sense to use the same mechanism for specifying the type of binary elements.

Also, since a binary spec is a collection of sorts, perhaps you can use dict syntax for the specifiers

<< foo :: [size: 1, utf8:true, binary:true, unit:8] >>

And perhaps just allow a single atom when it is a simple type, i.e. these would be equivalent

<< h, t :: binary >>
<< h, t :: [ binary: true ] >>
Owner

josevalim commented Aug 2, 2012

Thanks @krestenkrab, this is an interesting suggestion!

I like the idea of using the dict syntax, although it has the side effect of being more verbose.

About using the type specification operator, today the :: type operator is only allowed on @spec clauses (well, they are actually allowed anywhere, but @spec is the only place that actually uses them). Allowing them inside code, may muddy some concerns. For instance, one may expect to be able to use the same syntax you proposed in a @spec clause, to specify the binary format:

@spec foo(<< bin :: [size: 1, utf8:true, binary:true, unit:8] >>)

I have some other ideas flying around about using :: with code, so maybe it would be worth to take a look at those as well and see how they fit overall.

Contributor

krestenkrab commented Aug 2, 2012

Agreed, dict syntax is a bit more verbose; but it always annoyed me that bit-spec syntax was "special". It seems to be a corner case of the language, that has a disproportionate amount of syntax complexity. With dict syntax, you would only have to worry about the content, not the syntax. Those special binary-spec keys are hard enough to remember as it is :-)

I dislike that Erlang -spec's are separate entities; making it error prone to edit parameter lists; as specs tend to get our of sync. Also, there is an element of repetition (extra syntax, and naming the function for which the spec applies), which is avoided with inline type specs. Also, to do proper Erlang code, you have to repeat the spec in edoc style.

Owner

josevalim commented Aug 2, 2012

Agreed, dict syntax is a bit more verbose; but it always annoyed me that bit-spec syntax was "special". It seems to be a corner case of the language, that has a disproportionate amount of syntax complexity. With dict syntax, you would only have to worry about the content, not the syntax. Those special binary-spec keys are hard enough to remember as it is :-)

Well said.

I dislike that Erlang -spec's are separate entities; making it error prone to edit parameter lists; as specs tend to get our of sync. Also, there is an element of repetition (extra syntax, and naming the function for which the spec applies), which is avoided with inline type specs.

Yes, agreed. But simply allowing inline specs with the function definition would fix the separate entities but not fix the repetition. This is a problem I pondered for some time, but ultimately gave up because of the conflicts between specs and guards. For example, I would expect this clause to only be invoked if baz was an orddict, however, there is absolutely no way to ensure it, because guards are limited:

def foo(bar, baz :: orddict)
Owner

josevalim commented Aug 27, 2012

@yrashk can i get your opinion on this pls?

Contributor

yrashk commented Aug 28, 2012

On binaries or inline typespecs?

Owner

josevalim commented Aug 28, 2012

On the new syntax for binaries.

Contributor

yrashk commented Aug 28, 2012

I am personally not very convinced the new syntax is really that necessary. It takes us further away from erlang's syntax while not offering a substantial return.

Owner

josevalim commented Aug 28, 2012

The syntax originally proposed on the top is closer to erlang and less verbose imo. Today we have:

<< foo |  { :size, 1 } - :utf8 - :binary -  { :unit, 8 } >>

My favorite proposal is:

<< foo // size(1) - utf8 - binary - unit(8) >>

Which in Erlang is:

<< Foo:1 / utf8 - binary - unit:8 >>
Contributor

yrashk commented Aug 28, 2012

// implies "default value", doesn't it?

Owner

josevalim commented Aug 28, 2012

Right. Since // is only used on function signatures, we wouldn't have a
conflict. So we could "overload" the operator. After all, Erlang does it
with / in this case.

Contributor

yrashk commented Aug 28, 2012

It does, but it doesn't strike me as a great idea to overload this particular operator.

Contributor

krestenkrab commented Aug 28, 2012

If syntax is different from Erlang (and the rest of Elixir), why not have something simpler with commas in stead of those strange -s ?

<< foo // [ size(1), utf8, binary, unit(8) ] >>

The minuses doesn't look like a list of options.

Contributor

yrashk commented Aug 28, 2012

I side with @krestenkrab here, I find dashes irritable here

Owner

josevalim commented Aug 28, 2012

Yeah, I like it too. Expanding on
@krestenkrabhttps://github.com/krestenkrab proposal,
we could make it a keywords list (as he previously said too):

<< foo // [ size: 1, utf8: true, binary: true, unit: 8 ] >>

But I prefer a list of function calls because it is more concise:

<< foo // [ size(1), utf8, binary, unit(8) ] >>

Which one do you prefer?

Contributor

yrashk commented Aug 28, 2012

2nd one

Contributor

krestenkrab commented Aug 28, 2012

Same here. the keyword list proposal seems too mongo-ish :-)

Contributor

manpages commented Aug 28, 2012

I'm sorry to get into discussion, but just out of the plain curiosity — why //, not |?
(Just in case someone cares — I like second proposed variant more than first one as well)

Owner

josevalim commented Aug 28, 2012

Hello @manpages, I would like to reserve | in binaries to work similarly to lists:

<< h | t >>

Is a shortcut to:

<< h, t // [binary] >>

Elixir advocates the use of binaries for strings, so we need to make iterating binaries more convenient for the cases we need it.

Contributor

devinus commented Sep 9, 2012

I know my opinion doesn't hold much weight, but I like the idea of using :: as in Erlang. // already represents default value, | to me already has meaning, / doesn't make any sense. It's immediately obvious to me that :: represents "this is of type X" like in Erlang.

Owner

josevalim commented Sep 10, 2012

I have some reserves about using :: because it is used in type specifications and it may lead people to believe :: in binaries is some kind of type specification when it is a completely different thing, a bitstring specification. Using // may remind people of the default value, but it is quickly discarded as different things because it doesn't much make much sense in the given context.

Owner

josevalim commented Sep 10, 2012

Just to conclude the previous argument, given that both formats will be supported:

# Passing one specifier is simple
<< foo // size(1) >>

# For more than one, use a list
<< foo // [ size(1), utf8, binary, unit(8) ] >>

Now written using :::

# Passing one specifier is simple
<< foo :: size(1) >>

# For more than one, use a list
<< foo :: [ size(1), utf8, binary, unit(8) ] >>

The first example using :: is extremely similar to a type definition and may lead people to believe there is such thing as a size() type. I believe using // carry less ambiguity.

josevalim closed this in 2620444 Sep 10, 2012

Owner

josevalim commented Sep 10, 2012

The new syntax is implemented. And despise everything I said, I have decided to go with :: instead of //. The main reason is because in such example:

<< foo :: binary >>

binary is not a variable, it is not a function call, i.e. it is not executable elixir code. And this is exactly the idea conveyed by ::. Please give it a try and let me know how it goes!

Contributor

devinus commented Sep 10, 2012

@josevalim I'm glad you went with that! :) To me, :: very clearly conveys "of type X".

dch commented Oct 5, 2012

In the getting started guide http://elixir-lang.org/getting_started/2.html the bitstring syntax hasn't been updated. I'm sure this is trivial but I did not have the examples working simply by replacing | with :: .

Owner

josevalim commented Oct 5, 2012

@dch yeah, the guides contain the syntax for stable, we have updated guides in the next branch with the new syntax, they are just not published (yet).

Contributor

krestenkrab commented Oct 5, 2012

Perhaps I should repeat my question because I don't understand: why not allow inline type specs in function declarations?

Erlang types are optional and compile time only, and that's the case in many other language system (objective-c, Dart, and now even Microsoft's TypeScript).

It's so annoying (and error prone) to write the argument list twice (or three times if you also do a doc spec).

That seems like a good way to make it easier to document intentions.

Contributor

yrashk commented Oct 5, 2012

I suppose it would make sense to open a new issue about inline type specs. As the one responsible for bringing the initial implementation of typespecs to the language, I think inlining specs is an interesting idea.

I suppose we can have something like

def f(a :: integer)

this will need direct support from def/defp implementation, but yeah... @josevalim ?

Contributor

devinus commented Oct 5, 2012

@krestenkrab @yrashk The problem is that they aren't really "types." They're more like static contracts. This is why they aren't part of the function head.

Can you imagine writing,

def f(a :: :undefined | :foo | :bar)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment