Must and May convention

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
59 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

zxq9-2
On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
> I am able to get along fine without it, but if working without a similar
> construct requires 40 posts in a mailing list to make sure other people
> get a solution or pattern they are comfortable with when the operator is
> *not* available, then maybe there's a need to look at ways to improve
> the current state of things.

I get your point, but this is a fantastic mischaracterization of what is
happening.

So what IS actually happening?

We have an explosion of fad languages that keep adding sexy syntax all
over the place. People get sort of used to a few of them and then get
surprised when something new (like matching) is central to this new one
they're not used to, but feel like things are somehow wrong if one they
were expecting (like a composition operator, or objects, or whatever) is
missing.

What is happening in that fantabulous language space?

The languages are dying out, suffocating under their own weight and
semantic confusion -- leading to the sudden emergence of new sexy
languages.

This is actually AWESOME, overall, for the future of programming.
Eventually we are definitely going to figure out on something that is
just flat out Right for a wide variety of general cases.

But it is actually REALLY BAD for maintenance, writing production code,
onboarding new people who need to be productive, and the stability of
languages overall. Languages don't really evolve -- they become new,
subtly incompatible languages.

Erlang's focus is production, not research. It cannot afford to be a
fad language. So yes, arguing AGAINST changing it should be a stringent,
even a knee-jerk reaction to any new suggestions. Erlang works quite well
in a shocking variety of domains. Anything new threatens that. New
suggestions absolutely should have to stand up to withering criticism
over and over until their underlying merit finally wins out.

Python is in the same boat. "40 posts in a mailing list"... have you
seen the extreme reluctance Guido takes to adding new features? There is
a reason. And Python is better for it, still remains consistent, and
has weathered the language storms to the point that it is emerging as
the new Java.

That didn't happen by adding stuff because it was hip at the moment,
considered cool in some other language, or whatever. He was serious about
the idea that "there should be one, preferrably obvious, way to do it".
Over time it really looks like this has been the more powerful philosophy.

This thread wasn't even about adding a new feature or keeping one out.
It was about a coding convention Joe ran by us all that sparked a
conversation which attracted the predictable Elixir straphanger who needed
to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
That's great for Elixir, but a bad fit for Erlang if the only reason is
to add a "me, too" feature.

I'm not against language advancement, but I am against flippant language
changes. I will play Devil's Advocate for a year, even against features
I think would personally benefit ME, simply because language changes VERY
OFTEN have unintended consequences later that are just super hard to
square after the fact (well, impossible, really). Language design and
syntax are super tricky subjects once you get outside pure lambda calculus.
Let's let the exploration bonanza continue -- elsewhere.

When it is time to introduce some of these slick features it will be time
for an Erlang 2 (well, called something else hopefully). I do not believe
that language is Elixir, though. It has shown us a lot, but its beginning
to remind me of why Ruby eventually fell apart.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Vans S

>> Adding temporary variables is also nice because you
>can print them when things go wrong.

> Elixir has IO.inspect which you can use to tap into the middle of a pipe chain to output the intermediate state. (A form of the Kestrel combinator iirc)

This is exactly my biggest problem with elixirs pipe operator and why I do not feel comfortable using it except in
the most safest of cases.  It makes debugging and tracking errors more awkward, and I often found myself breaking
long pipes into temporary variables so I can inspect them. Multiple assignment kind of remedies the need for pipes.

Users = lists:filter(fun/1, Users)
Users = lists:map(fun/1, Users)
Users = lists:sort(fun/1, Users)

> So the |> pipe operator is not really perfect in elixir:

> - it is strict with regards to position of the arguments
> - it obscures the original number of arguments to a function (hello 
>   tracing)
> - it may compose or nest funny in some cases or other constructs
> - it is limited in what it can do (see my previous post in this thread)
> - it brings the language down a tricky avenue since other similar 
>   control flow constructs would need to add more special operators with 
>   new semantics
> - other language features may be equivalent to it already

Agree with most of this and want to add you can use anon functions
to remove the strictness of position of args

"5" = 
%{key: 5} 
  |> (&(:maps.get(:key, &1))).() 
  |> Integer.to_string

Piping to &:maps.get(:key, &1) does not work and gives a deprecation warnings
so it leaves me to wonder if it worked before, as its much simpler to read.

Going back to

{ok, Bin} | {error, Reason} = File:read("my_file")
 VS
Bin | throw/1 = File:read!("my_file")

After a good nights sleep I think I will side with this behavior:

If we were to write:

{ok, Bin} = File:read("my_file") 

we should instead write: 

Bin = File:read!("my_file") 
 OR 
Bin = MUST File.read("my_file") 
 OR..
you get the idea.


Because if File:read/1 returns an error we crash anyways due to our pattern match, and
the only element in position 1 would be 'ok'.

If we were to write case File:read("myfile") do, we should get the {ok, Bin}.

Now the 3rd case is where we store a local variable to case on, 

Res = File:read("my_file")
case Res of
  ..
end

To support all 3+ cases we need full control.  For example sometimes I dont like that
:global.register_name crashes instead of returning me a error tuple. Other times I welcome
the crash.


On Thursday, September 28, 2017 11:59 AM, zxq9 <[hidden email]> wrote:


On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
> I am able to get along fine without it, but if working without a similar
> construct requires 40 posts in a mailing list to make sure other people
> get a solution or pattern they are comfortable with when the operator is
> *not* available, then maybe there's a need to look at ways to improve
> the current state of things.

I get your point, but this is a fantastic mischaracterization of what is
happening.

So what IS actually happening?

We have an explosion of fad languages that keep adding sexy syntax all
over the place. People get sort of used to a few of them and then get
surprised when something new (like matching) is central to this new one
they're not used to, but feel like things are somehow wrong if one they
were expecting (like a composition operator, or objects, or whatever) is
missing.

What is happening in that fantabulous language space?

The languages are dying out, suffocating under their own weight and
semantic confusion -- leading to the sudden emergence of new sexy
languages.

This is actually AWESOME, overall, for the future of programming.
Eventually we are definitely going to figure out on something that is
just flat out Right for a wide variety of general cases.

But it is actually REALLY BAD for maintenance, writing production code,
onboarding new people who need to be productive, and the stability of
languages overall. Languages don't really evolve -- they become new,
subtly incompatible languages.

Erlang's focus is production, not research. It cannot afford to be a
fad language. So yes, arguing AGAINST changing it should be a stringent,
even a knee-jerk reaction to any new suggestions. Erlang works quite well
in a shocking variety of domains. Anything new threatens that. New
suggestions absolutely should have to stand up to withering criticism
over and over until their underlying merit finally wins out.

Python is in the same boat. "40 posts in a mailing list"... have you
seen the extreme reluctance Guido takes to adding new features? There is
a reason. And Python is better for it, still remains consistent, and
has weathered the language storms to the point that it is emerging as
the new Java.

That didn't happen by adding stuff because it was hip at the moment,
considered cool in some other language, or whatever. He was serious about
the idea that "there should be one, preferrably obvious, way to do it".
Over time it really looks like this has been the more powerful philosophy.

This thread wasn't even about adding a new feature or keeping one out.
It was about a coding convention Joe ran by us all that sparked a
conversation which attracted the predictable Elixir straphanger who needed
to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
That's great for Elixir, but a bad fit for Erlang if the only reason is
to add a "me, too" feature.

I'm not against language advancement, but I am against flippant language
changes. I will play Devil's Advocate for a year, even against features
I think would personally benefit ME, simply because language changes VERY
OFTEN have unintended consequences later that are just super hard to
square after the fact (well, impossible, really). Language design and
syntax are super tricky subjects once you get outside pure lambda calculus.
Let's let the exploration bonanza continue -- elsewhere.

When it is time to introduce some of these slick features it will be time
for an Erlang 2 (well, called something else hopefully). I do not believe
that language is Elixir, though. It has shown us a lot, but its beginning
to remind me of why Ruby eventually fell apart.

-Craig

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Fred Hebert-2
In reply to this post by zxq9-2
On 09/29, zxq9 wrote:

>On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
>> I am able to get along fine without it, but if working without a similar
>> construct requires 40 posts in a mailing list to make sure other people
>> get a solution or pattern they are comfortable with when the operator is
>> *not* available, then maybe there's a need to look at ways to improve
>> the current state of things.
>
>I get your point, but this is a fantastic mischaracterization of what is
>happening.
>

I'm not necessarily speaking of this specific thread. This conversation
is neither the first nor the last one about this. At work I've recetly
trained a team of 12 people into using Erlang and the questions about
the patterns in this thread have been had there as well. And similarly
for questions from the previous threads -- whether exceptions are the
right tool when it comes to validation.

>So what IS actually happening?
>
>We have an explosion of fad languages that keep adding sexy syntax all
>over the place. People get sort of used to a few of them and then get
>surprised when something new (like matching) is central to this new one
>they're not used to, but feel like things are somehow wrong if one they
>were expecting (like a composition operator, or objects, or whatever) is
>missing.
>
>What is happening in that fantabulous language space?

The 'fad' languages are very interesting testing grounds for ideas. A
thing like the pipe operator has had many forms, but I'm not sure I'd
call them a fad. They have existed in some form of other as:

- monads in Haskell and MLs
- pipes in Unix command lines and bash
- Mathematica has // (@ in Wolfram's)
- Some schemes have the (| ... ) syntax
- Clojure has the (-> ... ) threading construct
- Elixir has the |> operator
- method chaining in OO languages can kind of be seen as a way to get
  going there

It's not like there's 0 prior art and that one is taking incredible
hipster risk by looking at the feasability of such things.

>Erlang's focus is production, not research. It cannot afford to be a
>fad language. So yes, arguing AGAINST changing it should be a stringent,
>even a knee-jerk reaction to any new suggestions. Erlang works quite well
>in a shocking variety of domains. Anything new threatens that. New
>suggestions absolutely should have to stand up to withering criticism
>over and over until their underlying merit finally wins out.

Erlang's seat at the junction of research and industry is actually one
of the things that brought me here and let me stay. Saying that Erlang
is not really a research tool is blatantly wrong. There's a crapload of
very interesting papers coming out all the time, of tools at the edge of
research coming out, and they're one of the best thing to ever come out
of this community.

- Property-based testing has seen major improvements when being used
  under Erlang
- Dialyzer's approach to type analysis is pretty damn interesting and
  comes specifically from that junction between industry and research
- Concuerror and CutEr are other really nifty tools pushing the edge of
  a lot of things (generating functions that break your code? yes
  please!)
- Some of the stuff you see in the seq_trace modules were years ahead of
  what distributed logging would be out there; eventually people caught
  up
- Mnesia once was quite the innovative database
- The process model used in the VM is still quite unlike anything else
  out there

Granted the language itself is not a research testing ground the way
lisps or MLs have been, but the way it could position itself in the
industry came from using cutting edge technology ("Erlang had this 20
years ago!") with the care of long-term production engineering in mind.

If you want to see what a conservative language making conservative
decisions can yield, look at Go. The decisions they made are not
necessarily wrong (though I don't agree with them), but very little in
Go could be said to be innovative. In fact, a lot of the approaches they
have taken 6-7 years ago were already old news almost decades-old to
Erlang developers 10-15 years ago.

>
>Python is in the same boat. "40 posts in a mailing list"... have you
>seen the extreme reluctance Guido takes to adding new features? There is
>a reason. And Python is better for it, still remains consistent, and
>has weathered the language storms to the point that it is emerging as
>the new Java.
>
>That didn't happen by adding stuff because it was hip at the moment,
>considered cool in some other language, or whatever. He was serious about
>the idea that "there should be one, preferrably obvious, way to do it".
>Over time it really looks like this has been the more powerful philosophy.
>

Python is a case study of its own. The 2.x to 3.x migration is still not
complete 10 years in for the community. Java is still thriving (I don't
think it has lost users, but the industry has grown around it without
Java necessarily losing much in absolute terms).  C# is adding
absolutely interesting stuff that they experiment with in F# before
bringing it back into the main language.

Languages that never changes are those that die out. Who's thrilled
about using C before C99? But the changes must be worthwhile and
careful.

>This thread wasn't even about adding a new feature or keeping one out.
>It was about a coding convention Joe ran by us all that sparked a
>conversation which attracted the predictable Elixir straphanger who needed
>to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
>That's great for Elixir, but a bad fit for Erlang if the only reason is
>to add a "me, too" feature.
>

Have I ever mentioned "we should have it because Elixir does?"

I'm rather saying "we should possibly consider a form of it based on
observations in other language communities."

>I'm not against language advancement, but I am against flippant language
>changes. I will play Devil's Advocate for a year, even against features
>I think would personally benefit ME, simply because language changes VERY
>OFTEN have unintended consequences later that are just super hard to
>square after the fact (well, impossible, really). Language design and
>syntax are super tricky subjects once you get outside pure lambda calculus.
>Let's let the exploration bonanza continue -- elsewhere.
>
>When it is time to introduce some of these slick features it will be time
>for an Erlang 2 (well, called something else hopefully). I do not believe
>that language is Elixir, though. It has shown us a lot, but its beginning
>to remind me of why Ruby eventually fell apart.
>

Ruby has not yet fallen apart. Mostly, people are migrating away from it
because other languages are now offering a better future than what Ruby
currently offers.

If anything, a "let's put it in Erlang2" approach, based on current
evidence, is much more likely to be horrible and difficult (modula-3,
python3 or perl6 style) than progressive changes being incorporated into
the language on an ongoing basis.

I'm not going all in for the syntax changes either; these conversations
are necessary and it's a good thing to be having them.

But I think you're missing the forest from the trees if you don't think
the experimenting in other languages is not already going full steam
ahead. The question I'm asking is whether it would be time to source the
work of other places yet, if there's a form of it that has so far shown
itself to be adequate. The work has been ongoing for years already.


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

zxq9-2
On 2017年09月28日 木曜日 12:32:48 Fred Hebert wrote:
> If anything, a "let's put it in Erlang2" approach, based on current
> evidence, is much more likely to be horrible and difficult (modula-3,
> python3 or perl6 style) than progressive changes being incorporated into
> the language on an ongoing basis.

I'm on the fence here. I've been on both sides of Python migration and
don't see the move to Python 3 as uniformly bad -- but it was disruptive.
That's what I was getting at by saying "Erlang2" should be called
something different -- and not actually have anything to do with actual
Erlang as put out by the OTP team, imo.

(On that note, I really like the way LFE has made itself a lightweight
presence...)

> I'm not going all in for the syntax changes either; these conversations
> are necessary and it's a good thing to be having them.
>
> But I think you're missing the forest from the trees if you don't think
> the experimenting in other languages is not already going full steam
> ahead. The question I'm asking is whether it would be time to source the
> work of other places yet, if there's a form of it that has so far shown
> itself to be adequate. The work has been ongoing for years already.

No disagreement here. This is spot on.

We're definitely leaning toward violent agreement -- as usually winds up
being the case. :-)

But, as you have already known for a while, I do tend to beat the Caution
Drum when it comes to diddling with syntax.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Vans S
In reply to this post by Fred Hebert-2
> If anything, a "let's put it in Erlang2" approach, based on current 
> evidence, is much more likely to be horrible and difficult (modula-3, 
> python3 or perl6 style) than progressive changes being incorporated into 
> the language on an ongoing basis.

The problem with python 3 is code cannot be run on a python 2 interpreter.

They royally messed this up by not having backwards compatibility and hence why the transition is 
so long. OTP seems very good with depreciation and ensuring backwards compatibility.

I welcome a fully backwards compatible Erlang2, and Erlang3/4/5, as long as we don't have
another running Erlang2 from Erlang1 crisis (*cough* elixir).

If at the end of the day Erlang2 will generate erlang1 compatible beam files everything should be well,
then just pick a syntax/language you like (erlang2/3/4/5/9000), compile the beams, run them on BEAM.

The big problem with why elixir kind of "poisoned" the community in regards to having spinoff languages 
using beam is because they messed up the elixir compiler.  1-2 features ruined the whole thing and
made it a royal mess.  Those 2 features are multiple modules per file, and macros.  The cost of
having these 2 features which are rarely used is very high, to the point elixir cant even hotload properly
without fully purging modules (crashes during hotload if you call into a purged module at exact time elixir is
hotloading).




On Thursday, September 28, 2017 12:33 PM, Fred Hebert <[hidden email]> wrote:


On 09/29, zxq9 wrote:

>On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
>> I am able to get along fine without it, but if working without a similar
>> construct requires 40 posts in a mailing list to make sure other people
>> get a solution or pattern they are comfortable with when the operator is
>> *not* available, then maybe there's a need to look at ways to improve
>> the current state of things.
>
>I get your point, but this is a fantastic mischaracterization of what is
>happening.
>

I'm not necessarily speaking of this specific thread. This conversation
is neither the first nor the last one about this. At work I've recetly
trained a team of 12 people into using Erlang and the questions about
the patterns in this thread have been had there as well. And similarly
for questions from the previous threads -- whether exceptions are the
right tool when it comes to validation.

>So what IS actually happening?
>
>We have an explosion of fad languages that keep adding sexy syntax all
>over the place. People get sort of used to a few of them and then get
>surprised when something new (like matching) is central to this new one
>they're not used to, but feel like things are somehow wrong if one they
>were expecting (like a composition operator, or objects, or whatever) is
>missing.
>
>What is happening in that fantabulous language space?

The 'fad' languages are very interesting testing grounds for ideas. A
thing like the pipe operator has had many forms, but I'm not sure I'd
call them a fad. They have existed in some form of other as:

- monads in Haskell and MLs
- pipes in Unix command lines and bash
- Mathematica has // (@ in Wolfram's)
- Some schemes have the (| ... ) syntax
- Clojure has the (-> ... ) threading construct
- Elixir has the |> operator
- method chaining in OO languages can kind of be seen as a way to get
  going there

It's not like there's 0 prior art and that one is taking incredible
hipster risk by looking at the feasability of such things.

>Erlang's focus is production, not research. It cannot afford to be a
>fad language. So yes, arguing AGAINST changing it should be a stringent,
>even a knee-jerk reaction to any new suggestions. Erlang works quite well
>in a shocking variety of domains. Anything new threatens that. New
>suggestions absolutely should have to stand up to withering criticism
>over and over until their underlying merit finally wins out.

Erlang's seat at the junction of research and industry is actually one
of the things that brought me here and let me stay. Saying that Erlang
is not really a research tool is blatantly wrong. There's a crapload of
very interesting papers coming out all the time, of tools at the edge of
research coming out, and they're one of the best thing to ever come out
of this community.

- Property-based testing has seen major improvements when being used
  under Erlang
- Dialyzer's approach to type analysis is pretty damn interesting and
  comes specifically from that junction between industry and research
- Concuerror and CutEr are other really nifty tools pushing the edge of
  a lot of things (generating functions that break your code? yes
  please!)
- Some of the stuff you see in the seq_trace modules were years ahead of
  what distributed logging would be out there; eventually people caught
  up
- Mnesia once was quite the innovative database
- The process model used in the VM is still quite unlike anything else
  out there

Granted the language itself is not a research testing ground the way
lisps or MLs have been, but the way it could position itself in the
industry came from using cutting edge technology ("Erlang had this 20
years ago!") with the care of long-term production engineering in mind.

If you want to see what a conservative language making conservative
decisions can yield, look at Go. The decisions they made are not
necessarily wrong (though I don't agree with them), but very little in
Go could be said to be innovative. In fact, a lot of the approaches they
have taken 6-7 years ago were already old news almost decades-old to
Erlang developers 10-15 years ago.

>
>Python is in the same boat. "40 posts in a mailing list"... have you
>seen the extreme reluctance Guido takes to adding new features? There is
>a reason. And Python is better for it, still remains consistent, and
>has weathered the language storms to the point that it is emerging as
>the new Java.
>
>That didn't happen by adding stuff because it was hip at the moment,
>considered cool in some other language, or whatever. He was serious about
>the idea that "there should be one, preferrably obvious, way to do it".
>Over time it really looks like this has been the more powerful philosophy.
>

Python is a case study of its own. The 2.x to 3.x migration is still not
complete 10 years in for the community. Java is still thriving (I don't
think it has lost users, but the industry has grown around it without
Java necessarily losing much in absolute terms).  C# is adding
absolutely interesting stuff that they experiment with in F# before
bringing it back into the main language.

Languages that never changes are those that die out. Who's thrilled
about using C before C99? But the changes must be worthwhile and
careful.

>This thread wasn't even about adding a new feature or keeping one out.
>It was about a coding convention Joe ran by us all that sparked a
>conversation which attracted the predictable Elixir straphanger who needed
>to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
>That's great for Elixir, but a bad fit for Erlang if the only reason is
>to add a "me, too" feature.
>

Have I ever mentioned "we should have it because Elixir does?"

I'm rather saying "we should possibly consider a form of it based on
observations in other language communities."

>I'm not against language advancement, but I am against flippant language
>changes. I will play Devil's Advocate for a year, even against features
>I think would personally benefit ME, simply because language changes VERY
>OFTEN have unintended consequences later that are just super hard to
>square after the fact (well, impossible, really). Language design and
>syntax are super tricky subjects once you get outside pure lambda calculus.
>Let's let the exploration bonanza continue -- elsewhere.
>
>When it is time to introduce some of these slick features it will be time
>for an Erlang 2 (well, called something else hopefully). I do not believe
>that language is Elixir, though. It has shown us a lot, but its beginning
>to remind me of why Ruby eventually fell apart.
>

Ruby has not yet fallen apart. Mostly, people are migrating away from it
because other languages are now offering a better future than what Ruby
currently offers.

If anything, a "let's put it in Erlang2" approach, based on current
evidence, is much more likely to be horrible and difficult (modula-3,
python3 or perl6 style) than progressive changes being incorporated into
the language on an ongoing basis.

I'm not going all in for the syntax changes either; these conversations
are necessary and it's a good thing to be having them.

But I think you're missing the forest from the trees if you don't think
the experimenting in other languages is not already going full steam
ahead. The question I'm asking is whether it would be time to source the
work of other places yet, if there's a form of it that has so far shown
itself to be adequate. The work has been ongoing for years already.



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

John Krukoff
In reply to this post by zxq9-2
I recently read an interesting paper on inferring function purity in erlang, and thought it might be relevant to this discussion. As it does not appear to have been already shared, here is a link:

http://user.it.uu.se/~kostis/Papers/purity.pdf

The associated code sadly does not appear to still be maintained, though perhaps would also be of interest to someone following this discussion?

https://github.com/mpitid/purity

On Wed, Sep 27, 2017 at 7:50 PM, zxq9 <[hidden email]> wrote:
On 2017年09月27日 水曜日 12:46:19 Loïc Hoguin wrote:
> On 09/27/2017 11:08 AM, Joe Armstrong wrote:
> > For several years I've been using a convention in my hobby
> > projects. It's what I call the must-may convention.
> >
> > I'm wondering if it should be widely used.
> >
> > What is it?
> >
> > There are two commonly used conventions for handling bad arguments to
> > a function. We can return {ok, Val} or {error, Reason} or we can
> > return a value if the arguments are correct, and raise an exception
> > otherwise.
> >
> > The problem is that when I read code and see a function like
> > 'foo:bar(a,12)' I have no idea if it obeys one of these conventions or
> > does something completely different. I have to read the code to find
> > out.
> >
> > My convention is to prefix the function name with 'must_' or 'may_'
>
> I've been debating this in my head for a long time. I came to the
> conclusion that 99% of the time I do not want to handle errors.
> Therefore 99% of the functions should not return an error.

Taking this observation a step further...

I've got a guideline that has never made it into English yet (along with some coding guidelines and a few other things I should re-write...) that states that programs must always be refactored iteratively to aggregate side effects where possible and leave as much code functionally pure as can me managed.

The Rules:
- Pure functions are always crashers.
- Side-effecty functions retun the type `{ok, Value} | {error, Reason}`
- A side effect is anything that touches a resource external to the current function.

Some programs are full of side effects -- doing lots of network and file system I/O while running a GUI. Others are not so side-effecty. The case where you REALLY get viral side-effect proliferation is use of ETS tables (shared binaries is actually another case, but not included in the rule because the abstraction generally holds well enough). But even in these cases we can usually break the pure bits out somewhat cleanly, at least once we understand what the program really needs to do.

That bit right there, "understand what the program really needs to do", is the truly hard part of getting any of this right. Or anything right.

When a project starts from scratch you don't understand the details yet, otherwise typing speed would equate to development time and that's just never the case. So we start out with a very high proportion of {ok, V} | {error, R} type functions initially because we don't know anything about anything and side effects wind up getting scattered about because we just didn't have a very clear view of what was going on. When inheriting messy, legacy code you understand even LESS because you don't understand what the program should do and you also don't understand whatever it is currently doing until you diddle with it a bit.

And that's totally OK.

But only at first.

That's just to get us over the hump so that something works, the task is handled, and if a bus hit That One Guy tomorrow then we could continue along and at least have something running.

To avert a lifetime of nightmares, lost hair and broken marriages due to a death-march-style maintenance cycle, though, we pre-emptively attack the program again with a refactoring effort aimed specifically at unifying types and side-effect hygiene. It is common that you'll have two flavors of basically the same thing in different areas, especially if you've got more than two people working on the project. That's got to get fixed. It is also common, as noted above, that side effects are scattered about for various reasons.

Once we've shaken the easy bits out we sometimes add a list of pure functions to the top of each module as a module attribute:

-pure([foo/1, bar/2, baz/0]).

Those should not only be pure, provable, and excellent targets for the initial property testing effort to get warmed up on, but are also known to crash when they get bad inputs. And of course everything should, by this point, Dialyze cleanly. Also, it isn't impossible to write a tool that keeps track of impure calls and checks that the pure functions only ever make calls to other pure functions (vast swathes of the stdlib define abstract data types, and nearly all of these calls are pure).

What are the impure functions?

The service loop is always impure -- it deals with the mailbox. Socket handling functions (which may be the service loop as well). Anything that writes to a file. Anything that sends a message. Interface functions that wrap message passing. Anything that talks to wx or console I/O. Etc.

The outcome is that side effects traditionally get collected in:
- Interface functions
- Service loops (and mini-service loops / service states)
- File I/O wrapper
- Socket I/O wrapper
- User interfaec I/O wrapper

The last three are "wrappers" because by writing a wrapper we give ourselves a place to aggregate side effecty calls instead of making them in deeper code (out at the edges of the call graph). A message may come over a socket or into the service loop that requires some processing and then writing to a file, for example, but this doesn't mean that we write to the file out there at the bottom of the call graph. Instead, we call to get the processing done, then return the value back into the service loop (or somewhere close to it, like a message interpreter function), and then the next line will be a call to either write the file or a call to a file writer that is known to be side-effecty.

Just about everything else can be pure. Most of the time. (Of course, "processing a value" may involve network communication, or RPC, or asking some other stateful process to do the processing, and any of these can prevent a function from being pure. But it is rare that these are the majority of functions in a program.) That means almost everything can be written as a crashable function -- because the ones that return {ok, V} | {error, R} should have already known what they were dealing with before they called the pure functions.

One side effect of this overall process is that, at least in writing customer facing software, we discover errors straight away and fix them. Most of the bugs are the really simple kind:

"If I enter a number for a name, the window disappears and reappears with empty fields."
(The windows process crashed and restarted back where it was.)

or, more often

"If I enter a number as a name the name disappears after I click the 'submit' button."
(Something deeper in the system crashed and the final update to the GUI was never sent.)

We IMMEDIATELY know that we didn't type check there properly and some other part of the code died with the "bad" data once it was noticed -- and the user just saw a momentary hiccup and fixed whatever was wrong on their own. So this wasn't the end of the world or a big scary red X showing up on the screen with mysterious numbers and inscribed error messages or whatever. But it WAS bad and unexpected behavior for the most important person in the program's universe. A quick check of the crash log bears out what we thought, and that problem is from then on handled properly and never heard from again.

When this sort of problem becomes really confusing to debug is the cases where we've gotten too fancy with exception handling and played loose with types. That input value may have traveled quite far into the system before something died, and figuring it out is a bit more tricky then without a dead-obvious crash message letting you know about it.

Blah blah blah...

We are all looking at roughly the same things here. Joe likes to prefix function names. That's probably a good system, but it doesn't work well for people who use autocompletion (people still do that?). Is that a tooling conflict? Aren't Joe's function names THEMSELVES a sort of tool? How about the -pure declaration? That's great -- but what we really want, actually, is a way to declare a function pure so that Dialyzer could know about it, as part of the -spec for a function. That would be awesome. What happens for us is that functions near the top of a module tend to be side-effecty and functions at the bottom tend to be pure -- so we just sort of know what terrain we are navigating because we know the layout that results as an outcome of following our little side-effect focused refactoring. Also, in documentation we know the difference immediatly because of our own return typing convention: anything that returns naked values is a crasher, period.

It looks like none of the approaches is particularly perfect, though. I really wish Dialyzer accepted (and checked) explicit declarations of purity. I don't know what syntax would be good for this, but its something I would like to have. Also -- it would allow for people to maybe use their pure functions in guards, which is a frequent request I hear come up quite a bit.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Joe Armstrong-2
In reply to this post by zxq9-2
On Thu, Sep 28, 2017 at 5:59 PM, zxq9 <[hidden email]> wrote:

> On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
>> I am able to get along fine without it, but if working without a similar
>> construct requires 40 posts in a mailing list to make sure other people
>> get a solution or pattern they are comfortable with when the operator is
>> *not* available, then maybe there's a need to look at ways to improve
>> the current state of things.
>
> I get your point, but this is a fantastic mischaracterization of what is
> happening.
>
> So what IS actually happening?
>
> We have an explosion of fad languages that keep adding sexy syntax all
> over the place. People get sort of used to a few of them and then get
> surprised when something new (like matching) is central to this new one
> they're not used to, but feel like things are somehow wrong if one they
> were expecting (like a composition operator, or objects, or whatever) is
> missing.
>
> What is happening in that fantabulous language space?
>
> The languages are dying out, suffocating under their own weight and
> semantic confusion -- leading to the sudden emergence of new sexy
> languages.
>
> This is actually AWESOME, overall, for the future of programming.
> Eventually we are definitely going to figure out on something that is
> just flat out Right for a wide variety of general cases.
>
> But it is actually REALLY BAD for maintenance, writing production code,
> onboarding new people who need to be productive, and the stability of
> languages overall. Languages don't really evolve -- they become new,
> subtly incompatible languages.
>
> Erlang's focus is production, not research. It cannot afford to be a
> fad language. So yes, arguing AGAINST changing it should be a stringent,
> even a knee-jerk reaction to any new suggestions. Erlang works quite well
> in a shocking variety of domains. Anything new threatens that. New
> suggestions absolutely should have to stand up to withering criticism
> over and over until their underlying merit finally wins out.

I agree - years ago I argued against backwards compatibility - but
that was in the
days when if you wanted to change the language and break old code
we only had a few users and they were used to us breaking things.

The first doubts I had came a few years later - we were growing and
I wanted to break the system again by removing stuff that we should
never have put it.

I had an argument with a guy - over this.

"But we've tested the code"

"So change it and test again"

"Do you realise how difficult a test is?"

"No"

"We have to book the test facility 3-4 months in advance, they have to setup
it up - this takes 3-4 days (if we're lucky) then we run the tests a few hours
and they tear down the test setup - there's a lot of hardware involved."

The test facility had to booked months in advance and the tests cost a lot.
Full systems test are far more expensive that software testing .. .

Fast forward 30 years.

Amazing I can still compile and run erlang code I wrote 30 years ago - mostly
it works sometimes it needs very small changes.

When I run my old C (which worked) I am hit by a tsnami of warnings and errors.

There's a problem with software - really when we add something to a language.
We could have removed records when we added maps but this cause howls
of protest.

I think if you want to make a new dialect of Erlang (and it is a new
dialect and not a new language) then find - go ahead.

I while back I made erl2 to experiment with some changes to Erlang.
(see https://github.com/joearms/erl2)

X.erl2 files are transpiled into X.erl files -- this messes with and breaks
nothing.

If anybody wants to fix a bug they have to delve into erl2 and there is
no tooling emacs mode screws up, dialyzer etc cannot work on erl2
and so on.

Elixir is sufficiently different from Erlang that it superficially looks like
a different language - the funny thing (to me) is that this relative small
change in syntax turns out to turn what is perceived as "scary"
into something that is perceived as "familiar".

When I'm writing Elixir I keep thinking - "I wish I could do that in Erlang"
Pipes are nice - we talked about them.

I also like string interpolation

    io:format("Hello ~s it's ~s today",[Person,Weather])

 Sucks - the Variables are too far away from the formatting characters
so

   io:format("Hello #{Person} it's #{Raining} today")

would be much nicer


>
> Python is in the same boat. "40 posts in a mailing list"... have you
> seen the extreme reluctance Guido takes to adding new features? There is
> a reason. And Python is better for it, still remains consistent, and
> has weathered the language storms to the point that it is emerging as
> the new Java.
>
> That didn't happen by adding stuff because it was hip at the moment,
> considered cool in some other language, or whatever. He was serious about
> the idea that "there should be one, preferrably obvious, way to do it".
> Over time it really looks like this has been the more powerful philosophy.
>
> This thread wasn't even about adding a new feature or keeping one out.
> It was about a coding convention Joe ran by us all that sparked a
> conversation which attracted the predictable Elixir straphanger who needed
> to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
> That's great for Elixir, but a bad fit for Erlang if the only reason is
> to add a "me, too" feature.
>
> I'm not against language advancement, but I am against flippant language
> changes. I will play Devil's Advocate for a year, even against features
> I think would personally benefit ME, simply because language changes VERY
> OFTEN have unintended consequences later that are just super hard to
> square after the fact (well, impossible, really). Language design and
> syntax are super tricky subjects once you get outside pure lambda calculus.
> Let's let the exploration bonanza continue -- elsewhere.

I agree - there are many fun languages on the horizon.

Red, Nim, Crystal, Eve, Pony

>
> When it is time to introduce some of these slick features it will be time
> for an Erlang 2 (well, called something else hopefully). I do not believe
> that language is Elixir, though. It has shown us a lot, but its beginning
> to remind me of why Ruby eventually fell apart.

I'm not so sure - PHP is very popular - Visual Basic was so good
that Microsoft had to ruin it - Hypercard was so good that it is no longer
available. Programming in Borland turbo graphics was so fast and easy
that massive IDEs had to be invented.

Simple languages that encourage clear and correct code and do
no need armies of consultants to maintain into the future
are not very popular.

It's the "Nobody got sacked for using Microsoft" phenomena - there is
job security in using mainstream languages, and job security in writing and
fixing buggy code.

/Joe


>
> -Craig
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Vans S
> I agree - there are many fun languages on the horizon.
>
> Red, Nim, Crystal, Eve, Pony

This always leads me to think we should be writing engines, not languages.  A language should just be a syntax that compiles into an 
abstract form.  Too many of these new languages keep repeating the same things over and over in slightly different ways when it comes
to the internals.

> When I'm writing Elixir I keep thinking - "I wish I could do that in Erlang"
> Pipes are nice - we talked about them.
>
> I also like string interpolation
>
>    io:format("Hello ~s it's ~s today",[Person,Weather])
>
> Sucks - the Variables are too far away from the formatting characters
> so
>
>  io:format("Hello #{Person} it's #{Raining} today")
>
> would be much nicer

I really like the ideas behind this but working with the yrl seems to be an undocumented dark art, and very time consuming to 
learn.  There is so many things I would like to be able to do and to have the code compile to the same/functionally similar abstract form.

my_fun(atom, Map=#{bitfield_set=> _}) ->
        Value = maps:get(key, Map),
        Value2 = maps:get(key2, Map, undefined),
        Value3 = maps:get(key4, maps:get(key3, Map, #{}), undefined),
        List = io:format("the atom as a binary is ~p ~p", [atom, Value]),
        unicode:characters_to_binary(List).

    VS

fun my_fun(atom, Map=#{bitfield_set}) ->
    Value = Map[key]
    Value2 = Map[key2, undefined]
    Value3 = Map[key3][key4]
    "the atom as a binary is #{atom} #{Value}"
end


On Thursday, September 28, 2017 2:21 PM, Joe Armstrong <[hidden email]> wrote:


On Thu, Sep 28, 2017 at 5:59 PM, zxq9 <[hidden email]> wrote:

> On 2017年09月28日 木曜日 11:33:29 Fred Hebert wrote:
>> I am able to get along fine without it, but if working without a similar
>> construct requires 40 posts in a mailing list to make sure other people
>> get a solution or pattern they are comfortable with when the operator is
>> *not* available, then maybe there's a need to look at ways to improve
>> the current state of things.
>
> I get your point, but this is a fantastic mischaracterization of what is
> happening.
>
> So what IS actually happening?
>
> We have an explosion of fad languages that keep adding sexy syntax all
> over the place. People get sort of used to a few of them and then get
> surprised when something new (like matching) is central to this new one
> they're not used to, but feel like things are somehow wrong if one they
> were expecting (like a composition operator, or objects, or whatever) is
> missing.
>
> What is happening in that fantabulous language space?
>
> The languages are dying out, suffocating under their own weight and
> semantic confusion -- leading to the sudden emergence of new sexy
> languages.
>
> This is actually AWESOME, overall, for the future of programming.
> Eventually we are definitely going to figure out on something that is
> just flat out Right for a wide variety of general cases.
>
> But it is actually REALLY BAD for maintenance, writing production code,
> onboarding new people who need to be productive, and the stability of
> languages overall. Languages don't really evolve -- they become new,
> subtly incompatible languages.
>
> Erlang's focus is production, not research. It cannot afford to be a
> fad language. So yes, arguing AGAINST changing it should be a stringent,
> even a knee-jerk reaction to any new suggestions. Erlang works quite well
> in a shocking variety of domains. Anything new threatens that. New
> suggestions absolutely should have to stand up to withering criticism
> over and over until their underlying merit finally wins out.

I agree - years ago I argued against backwards compatibility - but
that was in the
days when if you wanted to change the language and break old code
we only had a few users and they were used to us breaking things.

The first doubts I had came a few years later - we were growing and
I wanted to break the system again by removing stuff that we should
never have put it.

I had an argument with a guy - over this.

"But we've tested the code"

"So change it and test again"

"Do you realise how difficult a test is?"

"No"

"We have to book the test facility 3-4 months in advance, they have to setup
it up - this takes 3-4 days (if we're lucky) then we run the tests a few hours
and they tear down the test setup - there's a lot of hardware involved."

The test facility had to booked months in advance and the tests cost a lot.
Full systems test are far more expensive that software testing .. .

Fast forward 30 years.

Amazing I can still compile and run erlang code I wrote 30 years ago - mostly
it works sometimes it needs very small changes.

When I run my old C (which worked) I am hit by a tsnami of warnings and errors.

There's a problem with software - really when we add something to a language.
We could have removed records when we added maps but this cause howls
of protest.

I think if you want to make a new dialect of Erlang (and it is a new
dialect and not a new language) then find - go ahead.

I while back I made erl2 to experiment with some changes to Erlang.
(see https://github.com/joearms/erl2)

X.erl2 files are transpiled into X.erl files -- this messes with and breaks
nothing.

If anybody wants to fix a bug they have to delve into erl2 and there is
no tooling emacs mode screws up, dialyzer etc cannot work on erl2
and so on.

Elixir is sufficiently different from Erlang that it superficially looks like
a different language - the funny thing (to me) is that this relative small
change in syntax turns out to turn what is perceived as "scary"
into something that is perceived as "familiar".

When I'm writing Elixir I keep thinking - "I wish I could do that in Erlang"
Pipes are nice - we talked about them.

I also like string interpolation

    io:format("Hello ~s it's ~s today",[Person,Weather])

Sucks - the Variables are too far away from the formatting characters
so

  io:format("Hello #{Person} it's #{Raining} today")

would be much nicer


>
> Python is in the same boat. "40 posts in a mailing list"... have you
> seen the extreme reluctance Guido takes to adding new features? There is
> a reason. And Python is better for it, still remains consistent, and
> has weathered the language storms to the point that it is emerging as
> the new Java.
>
> That didn't happen by adding stuff because it was hip at the moment,
> considered cool in some other language, or whatever. He was serious about
> the idea that "there should be one, preferrably obvious, way to do it".
> Over time it really looks like this has been the more powerful philosophy.
>
> This thread wasn't even about adding a new feature or keeping one out.
> It was about a coding convention Joe ran by us all that sparked a
> conversation which attracted the predictable Elixir straphanger who needed
> to mention that Elixir has pipe syntax and `with` that can do stuff. Neato.
> That's great for Elixir, but a bad fit for Erlang if the only reason is
> to add a "me, too" feature.
>
> I'm not against language advancement, but I am against flippant language
> changes. I will play Devil's Advocate for a year, even against features
> I think would personally benefit ME, simply because language changes VERY
> OFTEN have unintended consequences later that are just super hard to
> square after the fact (well, impossible, really). Language design and
> syntax are super tricky subjects once you get outside pure lambda calculus.
> Let's let the exploration bonanza continue -- elsewhere.

I agree - there are many fun languages on the horizon.

Red, Nim, Crystal, Eve, Pony

>
> When it is time to introduce some of these slick features it will be time
> for an Erlang 2 (well, called something else hopefully). I do not believe
> that language is Elixir, though. It has shown us a lot, but its beginning
> to remind me of why Ruby eventually fell apart.

I'm not so sure - PHP is very popular - Visual Basic was so good
that Microsoft had to ruin it - Hypercard was so good that it is no longer
available. Programming in Borland turbo graphics was so fast and easy
that massive IDEs had to be invented.

Simple languages that encourage clear and correct code and do
no need armies of consultants to maintain into the future
are not very popular.

It's the "Nobody got sacked for using Microsoft" phenomena - there is
job security in using mainstream languages, and job security in writing and
fixing buggy code.

/Joe


>
> -Craig
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Michał Muskała
On 28 Sep 2017, 21:04 +0200, Vans S <[hidden email]>, wrote:


I really like the ideas behind this but working with the yrl seems to be an undocumented dark art, and very time consuming to 
learn.  There is so many things I would like to be able to do and to have the code compile to the same/functionally similar abstract form.

my_fun(atom, Map=#{bitfield_set=> _}) ->
        Value = maps:get(key, Map),
        Value2 = maps:get(key2, Map, undefined),
        Value3 = maps:get(key4, maps:get(key3, Map, #{}), undefined),
        List = io:format("the atom as a binary is ~p ~p", [atom, Value]),
        unicode:characters_to_binary(List).

    VS

fun my_fun(atom, Map=#{bitfield_set}) ->
    Value = Map[key]
    Value2 = Map[key2, undefined]
    Value3 = Map[key3][key4]
    "the atom as a binary is #{atom} #{Value}"
end
 

The initial map proposal included a syntax for key access:

    Map#{Key}

But it was never actually implemented.

Michał.

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Joe Armstrong-2
On Thu, Sep 28, 2017 at 9:11 PM, Michał Muskała <[hidden email]> wrote:
> On 28 Sep 2017, 21:04 +0200, Vans S <[hidden email]>, wrote:
>
>
> I really like the ideas behind this but working with the yrl seems to be an
> undocumented dark art, and very time consuming to
> learn.

yecc is an erlang implementation of an LALR(1) parser. .yrl files
are pretty much like YACC files

In the 'old days computer science student learn LEX and YACC from the
"Dragon book"
https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools

The Erlang documentation is sparse since there is copious
documentation of YECC and LEX.

yecc and leex are more-or-less faithful copies of YACC and LEX - the only
difference is the productions are in Erlang not C.

To learn yecc and leex a good way is to find a YACC and LEX grammar
on the net and convert it to Erlang.

Have fun

/Joe


> There is so many things I would like to be able to do and to have
> the code compile to the same/functionally similar abstract form.
>
> my_fun(atom, Map=#{bitfield_set=> _}) ->
>         Value = maps:get(key, Map),
>         Value2 = maps:get(key2, Map, undefined),
>         Value3 = maps:get(key4, maps:get(key3, Map, #{}), undefined),
>         List = io:format("the atom as a binary is ~p ~p", [atom, Value]),
>         unicode:characters_to_binary(List).
>
>     VS
>
> fun my_fun(atom, Map=#{bitfield_set}) ->
>     Value = Map[key]
>     Value2 = Map[key2, undefined]
>     Value3 = Map[key3][key4]
>     "the atom as a binary is #{atom} #{Value}"
> end
>
>
>
> The initial map proposal included a syntax for key access:
>
>     Map#{Key}
>
> But it was never actually implemented.
>
> Michał.
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Richard A. O'Keefe-2
In reply to this post by Joe Armstrong-2


On 27/09/17 10:08 PM, Joe Armstrong wrote:
> For several years I've been using a convention in my hobby
> projects. It's what I call the must-may convention.

The problem with that convention is the English language.

"X must Y" means that either X does Y or there will be
dire consequences for X.  That's the one I would expect
to raise an exception.

"X should Y" means that X ought to do Y but can be
forgiven for failing.  That's the one I would expect
to return {ok,_} or {error,_}.

"X may Y" means that X might or might not Y, either way
I'm easy, failing to Y is not in any sense an error.
I'd expect a result like {aye,_} or nay, where neither
of these is an error.

So your convention is the very worse of what the words
suggest to me.  I would expect

     Data = must_read_file(File_Name)

and

     case should_read_file(File_Name)
       of {ok,Data} -> ...
        ; {error,Reason} -> ...
     end

A nice introduction to deontic logic (the logic of
must/may/should/mayn't and so on) can be found at
https://plato.stanford.edu/entries/logic-deontic/

RFC 2119 is probably of more direct relevance to Erlang
programmers.  https://www.ietf.org/rfc/rfc2119.txt says
(paraphrased)

MUST       an absolute requirement (do or die!)
MUST NOT   an absolute prohibition (die if you do!)
SHOULD     a recommendation (do it if you can)
SHOULD NOT a recommendation against (avoid it if you can)
MAY        an option; it's ok either way.

So I like the idea of such a convention, but I think it
should not confuse people familiar with RFC 2119.

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Richard A. O'Keefe-2
In reply to this post by Loïc Hoguin-3


On 27/09/17 11:46 PM, Loïc Hoguin wrote:

> I've been debating this in my head for a long time. I came to the
> conclusion that 99% of the time I do not want to handle errors.
> Therefore 99% of the functions should not return an error.
>
> What happens for the 1% of the time where I do want to handle an error
> and the function doesn't allow it? Well, I catch the exception.

+1

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

zxq9-2
In reply to this post by Joe Armstrong-2
On 2017年09月28日 木曜日 20:21:01 Joe Armstrong wrote:

> On Thu, Sep 28, 2017 at 5:59 PM, zxq9 <[hidden email]> wrote:
> > When it is time to introduce some of these slick features it will be time
> > for an Erlang 2 (well, called something else hopefully). I do not believe
> > that language is Elixir, though. It has shown us a lot, but its beginning
> > to remind me of why Ruby eventually fell apart.
>
> I'm not so sure - PHP is very popular - Visual Basic was so good
> that Microsoft had to ruin it - Hypercard was so good that it is no longer
> available. Programming in Borland turbo graphics was so fast and easy
> that massive IDEs had to be invented.
>
> Simple languages that encourage clear and correct code and do
> no need armies of consultants to maintain into the future
> are not very popular.
>
> It's the "Nobody got sacked for using Microsoft" phenomena - there is
> job security in using mainstream languages, and job security in writing and
> fixing buggy code.

OH NOES!!!

I'm so utterly uncomfortable with this whole last bit here. But I found
myself involuntarily laughing as I read this. That is indeed the shape
of things most of the time.

Hopefully we can find a way to break out of the tarpit.

Someday.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Richard A. O'Keefe-2
In reply to this post by Vans S


On 29/09/17 5:49 AM, Vans S wrote:
>
> The 'fad' languages are very interesting testing grounds for ideas. A
> thing like the pipe operator has had many forms, but I'm not sure I'd
> call them a fad. They have existed in some form of other as:

The pipe operator is a very shallow thing indeed.
% fsi
 > (|>);;
val it : ('a -> ('a -> 'b) -> 'b) = <fun:it@4>

There's really only one function with that type:
'T'(A, F) -> F(A).
It's the T combinator
(http://www.angelfire.com/tx4/cus/combinator/birds.html)
written as an infix operator.

The equivalent in Haskell is not monads but
flip ($), and the normal Haskell equivalent of
    a |> f |> g |> h
is just
    h $ g $ f $ a
or even
    (h . g . f) a

There really isn't anything special about |> in a
functional language.  The only real issue is whether
the syntax adapts well to user-defined combinators,
and Erlang syntax doesn't.  That's purely a syntax
issue and says nothing about the soundness of the basic
model.


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Richard A. O'Keefe-2
In reply to this post by Fred Hebert-2


On 29/09/17 4:33 AM, Fred Hebert wrote:

> As an example, Elixir has added the 'With' syntax:
>
>    opts = %{width: 10, height: 15}
>    with {:ok, width} <- Map.fetch(opts, :width),
>         {:ok, height} <- Map.fetch(opts, :height),
>      do: {:ok, width * height}

How is this different from 'let'?

There was a very old proposal for Erlang that
variables introduced between 'begin' ... 'end'
brackets should be local, so that this would be
     begin
         {ok,Width}  = map:fetch(Opts, width),
         {ok,Height} = map:fetch(Opts, height),
         {ok, Width*Height}
     end
If memory serves me, this was proposed about 25 years
ago, at about the same time that 'cond' was.

>
> This is now some kind of new fancy syntax. However, Erlang lets you do
> something similar to with with list comprehensions:
>
>    Opts = dict:from_list([{width,10}, {heigth,15}]),
>    hd([{ok, Width*Height}
>        || {ok, Width} <- [dict:find(width, Opts)],
>           {ok, Height} <- [dict:find(height, Opts)]])

Or with
     (fun ({ok,Width}, {ok,Height}) -> {ok,Width*Height} end
     )(map:fetch(Opts, width), map:fetch(Opts, height))

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Fred Hebert-2
On 09/29, Richard A. O'Keefe wrote:

>On 29/09/17 4:33 AM, Fred Hebert wrote:
>
>>As an example, Elixir has added the 'With' syntax:
>>
>>   opts = %{width: 10, height: 15}
>>   with {:ok, width} <- Map.fetch(opts, :width),
>>        {:ok, height} <- Map.fetch(opts, :height),
>>     do: {:ok, width * height}
>
>How is this different from 'let'?
>
>There was a very old proposal for Erlang that
>variables introduced between 'begin' ... 'end'
>brackets should be local, so that this would be
>    begin
>        {ok,Width}  = map:fetch(Opts, width),
>        {ok,Height} = map:fetch(Opts, height),
>        {ok, Width*Height}
>    end
>If memory serves me, this was proposed about 25 years
>ago, at about the same time that 'cond' was.
>

The distinction is in the control flow, rather than the scoping. With
the `with' construct (and the reason why I compared it with monadic
approaches), any value that represents an error returns and aborts the
computation.

The construct is rather equivalent to:

    case map:fetch(Opts, width) of
        {ok, Width} ->
            case map:fetch(Opts, height) of
                {ok, Height} ->
                    {ok, Width*Height};
                {error, T} ->
                    {error, T}
            end;
        {error, T} ->
            {error, T}
    end.

The idea being that repeated conditionals based on the type of return
(`{ok, Val} | {error, Term}`) could be abstracted over with a language
construct.

- The pipe (|>) can be the simplest one where a value is directly passed
- the 'maybe' approach can be a branching based on `{ok,T}` or `{error,
  T}` that picks whether to keep calling the waiting functions or
  whether to return directly

Variants could be imagined based on a requirement such as "connection is
active", or "file has not reached EOF", or others. I don't know that
they would be practical, but that's why I mentioned in earlier posts
that the monadic approach (bind + return with say Haskell's do notation)
could be an interesting model.

>>
>>This is now some kind of new fancy syntax. However, Erlang lets you do
>>something similar to with with list comprehensions:
>>
>>   Opts = dict:from_list([{width,10}, {heigth,15}]),
>>   hd([{ok, Width*Height}
>>       || {ok, Width} <- [dict:find(width, Opts)],
>>          {ok, Height} <- [dict:find(height, Opts)]])
>
>Or with
>    (fun ({ok,Width}, {ok,Height}) -> {ok,Width*Height} end
>    )(map:fetch(Opts, width), map:fetch(Opts, height))
>

Right. There's multiple equivalent forms. Yours right there is a bit
tricky because it's hard to make it "look nice" when you have, say, 15
operations to queue up. In fact for Erlang's validation in such cases,
the pattern I see the most often is just iterating over a list with many
clauses. See the SSL validation code for example:

https://github.com/erlang/otp/blob/56f6f1829e1f3fd3752914b302276bc9bf490bbb/lib/ssl/src/ssl.erl#L1311-L1390

This single 'validation' function can then be applied to each element of
a list of options; if one is bad, it has to throw or change the workflow
and abort; in some cases, it may transform the value, and in most cases,
it just stores it.

I'm curious to see if there isn't a better form for that approach.  
Currently the common forms would be:

- write a recursive function that handles everything (same as code
  above)
- write a higher order function that separates control flow from the
  validation logic (is_valid(OptName,Val) -> {store, Name, Val} |
  {abort, Reason}) and is applied in a more generic manner
- nested case ... of constructs
- a try ... catch where all 'aborts' are considered equal

Under current Erlang, I'd favor the second option for very large
sequences of operations; it's totally legit and regular functional
programming, even though a bit cumbersome to write.

Maybe what I'm really after is more of a 'folding comprehension' where
rather than mapping over the values of a list, an accumulator is being
handled.

<|NewState || InitState ||
   {ok, Entry} <- List,
   {ok, SubEntry} <- op(Entry),
   abort_if_false(Entry),
   proceed_if_true(SubEntry) |>

Sounds like it could be general enough to do whatever you want after
that. Under an abortive filter, Validation could then look like:

<| dict:store(K, V, D) || D <- dict:new() ||
   {ok, V} <- [dict:fetch(K, Opts)],
   is_valid(K, V) |>

And then there you go, computation complete.
Then again, the syntax isn't pretty, and the decision to pick 'abort' as
a default rather than 'skip' is similarly arbitrary and impossible to
validate.

Maybe we're just better off with the very long form stuff anyway. It's
unambiguous and straightforward after all, and that makes for
maintainable code.
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

zxq9-2
On 2017年09月29日 金曜日 08:53:38 Fred Hebert wrote:

> On 09/29, Richard A. O'Keefe wrote:
> >On 29/09/17 4:33 AM, Fred Hebert wrote:
> >
> >>As an example, Elixir has added the 'With' syntax:
> >>
> >>   opts = %{width: 10, height: 15}
> >>   with {:ok, width} <- Map.fetch(opts, :width),
> >>        {:ok, height} <- Map.fetch(opts, :height),
> >>     do: {:ok, width * height}
> >
> >How is this different from 'let'?
> >
> >There was a very old proposal for Erlang that
> >variables introduced between 'begin' ... 'end'
> >brackets should be local, so that this would be
> >    begin
> >        {ok,Width}  = map:fetch(Opts, width),
> >        {ok,Height} = map:fetch(Opts, height),
> >        {ok, Width*Height}
> >    end
> >If memory serves me, this was proposed about 25 years
> >ago, at about the same time that 'cond' was.
> >
>
> The distinction is in the control flow, rather than the scoping. With
> the `with' construct (and the reason why I compared it with monadic
> approaches), any value that represents an error returns and aborts the
> computation.
>
> The construct is rather equivalent to:
>
>     case map:fetch(Opts, width) of
>         {ok, Width} ->
>             case map:fetch(Opts, height) of
>                 {ok, Height} ->
>                     {ok, Width*Height};
>                 {error, T} ->
>                     {error, T}
>             end;
>         {error, T} ->
>             {error, T}
>     end.
>
> The idea being that repeated conditionals based on the type of return
> (`{ok, Val} | {error, Term}`) could be abstracted over with a language
> construct.
>
> - The pipe (|>) can be the simplest one where a value is directly passed
> - the 'maybe' approach can be a branching based on `{ok,T}` or `{error,
>   T}` that picks whether to keep calling the waiting functions or
>   whether to return directly
>
> Variants could be imagined based on a requirement such as "connection is
> active", or "file has not reached EOF", or others. I don't know that
> they would be practical, but that's why I mentioned in earlier posts
> that the monadic approach (bind + return with say Haskell's do notation)
> could be an interesting model.

Why would this not be a pipeline function in the lists module?

It is actually done that way in more than a few inner project libs I've
worked with.

As an aside, I've always wondered why the lists module doesn't have
any pipelines, considering how often this sort of discussion comes up
and how quickly and cleanly it is solved inside various projects with
a pipeline function (or set of them that have different behaviors).

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

Fred Hebert-2
On 09/29, zxq9 wrote:

>Why would this not be a pipeline function in the lists module?
>
>It is actually done that way in more than a few inner project libs I've
>worked with.
>
>As an aside, I've always wondered why the lists module doesn't have
>any pipelines, considering how often this sort of discussion comes up
>and how quickly and cleanly it is solved inside various projects with
>a pipeline function (or set of them that have different behaviors).
>

It could be. That's how the fancier forms are implemented in most cases
I've seen. The fancyflow forms I've mentioned earlier in the thread are
in fact just macros over to run:

    fancyflow:pipe(InitialState, [fun(Var) -> Exp1 end,
                                  fun(Var) -> Exp2 end,
                                  ...,
                                  fun(Var) -> ExpN end])
    fancyflow:maybe(InitialState, [fun(Var) -> Exp1 end,
                                   fun(Var) -> Exp2 end,
                                   ...,
                                   fun(Var) -> ExpN end])

Where there's nothing quite magical about them. At that point, they're
just a bit verbose and cumbersome to write. You're probably better off
not using the pipe function and just use variables like `X1 = Exp1, X2 =
Exp2, ..., XN = ExpN', and it will be shorter to read at a minimal
maintenance cost. The one place it gets to be nicer is when the
operations to be run are higher order functions to begin with, and not
just a local abstraction over literal code that would not require funs
otherwise.

The `maybe' function would probably still be worth it though. Nested
cases are annoying enough to be more costly to maintain than the
abstracted version.

I'm not quite sure 'lists' would be the module for them; they're kind of
flipping the concept of a 'fold' where you apply a function to multiple
list elements updating a single state to 'applying a list of functions
to a term'. They're definitely mappable to the lists:foldl operations,
but it feels like the 'lists' part of it is not really what matters
here, but the control flow is.


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Must and May convention

zxq9-2
On 2017年09月29日 金曜日 10:05:12 Fred Hebert wrote:

> It could be. That's how the fancier forms are implemented in most cases
> I've seen. The fancyflow forms I've mentioned earlier in the thread are
> in fact just macros over to run:
>
>     fancyflow:pipe(InitialState, [fun(Var) -> Exp1 end,
>                                   fun(Var) -> Exp2 end,
>                                   ...,
>                                   fun(Var) -> ExpN end])
>     fancyflow:maybe(InitialState, [fun(Var) -> Exp1 end,
>                                    fun(Var) -> Exp2 end,
>                                    ...,
>                                    fun(Var) -> ExpN end])

I see. I actually like to write them explicitly (without funs, where
possible) as:

    Steps =
        [fun change_thingy/2,
         fun check_thingy/2,
         fun do_other_thing/2],
    pipes:shortcircuit(Steps, Value).

But whenever I *really* need this sort of thing, the creation of `Steps`
winds up being dynamic and it is a pretty special situation. Most of the
time this stuff doesn't survive refactoring for one reason or another.

> The `maybe' function would probably still be worth it though. Nested
> cases are annoying enough to be more costly to maintain than the
> abstracted version.

This is the main one I see, or at least some form of it. Usually I run
into one of three variants of a `maybe`:

- A shortcut pipe that returns immediately when it hits {error, R} or
  'false'.

- A side-effecty pipe that accumulates errors and returns a list of them
  of the type [{error, term()}] in the order they were accumulated.

- A mapfold that permits a stack of operations on a stack of values.
  These usually shortcut a return right away and are pretty important
  to keep free of side effects (can be a complexity grenade otherwise).

> I'm not quite sure 'lists' would be the module for them; they're kind of
> flipping the concept of a 'fold' where you apply a function to multiple
> list elements updating a single state to 'applying a list of functions
> to a term'. They're definitely mappable to the lists:foldl operations,
> but it feels like the 'lists' part of it is not really what matters
> here, but the control flow is.

I can see that. I've actually got a convenience lib that includes a few
pipelines that I have yet to open source. I still haven't made up my mind
whether I really *like* these or not -- usually I have a few places where
I start with them, but after refactoring somewhere further upstream in
the flow of the program they wind up going away. Not because they are
marked for death, but just because they sort of melt away once other stuff
gets figured out more completely (particularly once data is understood
well enough that it finally flattens out, ADTs get built if needed, etc.).

That tendency is another reason I don't like special syntax for this,
because it seems to often be less useful in a mature project than in ones
where you haven't quite figured out what you're doing yet. Having a
special syntax sort of supports crystalization of convoluted code -- or at
least psychologically lends support to leaving a bunch of stuff clumped
together that *really* doesn't belong together once the code is understood
better. Special syntax to make prototype-grade code live longer seems like
a mistake to me.

Unwinding also tends to happen when I start attacking cases of

X1 = foo(X),
X2 = bar(X1),
...

That's a code smell that has actually never once unwound itself entirely
as other areas of a project have been cleaned up (particularly once data
abstractions have been put in place). At most I ever wind up with three
versions of a value if it is undergoing some complex update that involves
something like `lists:keytake/3` -- which is somewhat rare. What used to
be local transform assignments tend to wind up being changes happening
out in some other function somewhere and I just get the completed value
back. That's not always obvious at the outset of a project, though,
because a lot of time it is a bit indefinite what the right balance (and
level of need) for carrying some local state around might be to get the
job done. Those "refactoring for purity" [1] efforts is when all the
X1, X2, .. XN type variable names disappear if they had popped up in the
first place.

Regarding using lists:foldl/3 to build pipelines... I occasionally see
(and occasionally write) pipelines using lists:foldl/3 in prototype code,
knowing those bits almost certainly won't survive the next refactoring.
You read my mind...

[1] "purity" I feel sure this isn't a proper word!

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
123