[erlang-questions] Matthew Sackman's "cut" proposal

Richard O'Keefe ok@REDACTED
Tue Jul 12 00:59:24 CEST 2011


On 11/07/2011, at 9:15 PM, Tim Watson wrote:

> On 11 July 2011 06:34, Richard O'Keefe <ok@REDACTED> wrote:
>> 
>> Why did Pop-2 need partial application?
>> Because it used dynamic scope for variables, so that returning a lambda
>> would not have worked.
>> Why does Eiffel have agents?
>> Because it didn't have lambdas.
>> 
>> Erlang _does_ have working funs so we can do manual Currying on the rare
>> occasions when it's needed.
>> 
> 
> For me, the compelling reason to have *something* like this is that
> function composition and partial application are the bread and butter
> of my programming style,

This is not unlike saying "assignment and goto are the bread and
buffer of my C style, so Erlang should support assignment and goto".
Function composition and partial application are not part of
>>Erlang's<< native style.

Again, the old joke:
	Doctor: What's the problem?
	Patient: It hurts when I do <this>?
	Doctor: Then don't do <that>.

If you try to write Erlang as if it were SML (or CML),
it's obviously going to hurt.  So the answer is don't do that.

> and in Erlang they both prove to be rather,
> what shall we say, wordy? For example, consider this function (a
> hamcrest matcher) which isn't too bad at all, but is indicative of the
> amount of noise we have to deal with:
> 
> -spec(ends_with/1 :: (string()) -> fun((string()) -> boolean())).
> ends_with(X) ->
>    fun(Y) -> string:equal(string:right(Y, length(X)), X) end.

The amount of "noise" here is precisely three tokens: 'fun' '->' 'end'.
Your suffering cannot be said to be extreme.

As an occasional Haskell and SML programmer, I am certainly not
against Curried functions.  What I *am* against is an approach which
is ugly, confusing, and almost never applicable.

If Erlang syntax were generalised to

	ends_with(X)(Y) -> string:equal(string:right(Y, length(X)), X).

we could eliminate those three tokens.

What's more, we could do that *WITHOUT* any weird restrictions on
 - whether pattern matching is allowed in the fun
 - the form of the fun body
 - where the arguments appear in the fun body
 - the order in which the arguments appear in the fun body.
and *WITHOUT*
 - any weird non-intention-revealing tokens, whether '_' or '?'

> Not personally, I would much sooner have the compiler deal with the
> anonymous function machinery for me:
> 
> -spec(ends_with/1 :: (string()) -> fun((string()) -> boolean())).
> ends_with(X) ->
>    string:equal(string:right(?, length(X)), X) end.

It would be much much better as

ends_with(Suffix)(String) ->
    string:equal(string:right(String, length(Suffix)), Suffix).

> 
> Note that I've substituted '_' for ? in order to get away from

No, you substituted '?' for '_', or replaced '_' with '?'.

> discussing the syntax - I could happily go with any marker-character

My argument is that ANY marker character is wrong because
 - you can't nest closures that way (does this ? belong to the
   inner fun or the outer one?)
 - you can't read the intent of a parameter from a weirdo like
   '?' the way you can from a name like String
 - you have all those other weird restrictions I mentioned above
If you want to do Haskell-like things, it doesn't make sense not
to do it in a Haskell-like way, and that is to say that

	<name>(<args1>)(<args2>)...(<argsn>) -> <body>

is equivalent to

	<name>(<args1>) ->
	    fun (<args2>) ->
		...
		fun (<argsn>) ->
		    <body>
		end
		...
	    end

*NO* new tokens!
*NO* weird restrictions!
Good consistency with the way Clean and ML and Haskell do it.
*NO* changes elsewhere in the language.

> The other point about Mathew's work, is that it provides a means to
> eliminate trillions (ahem)

Ahem indeed.

> of intermediate (state) variables in a
> section of code such as:
> 
> doodle(X) ->
>    Result1 = do_thing(X),
>    Result2 = calculate_next_thing(Result1),
>    Result3 = get_bored_of_typing_result_x(Result2),
>    Result4 = start_getting_annoyed(Result3),
>    %% etc, etc, etc
>    {ok, Result23}.
> 
> Yes I know that "explaining variables" are better than silly and
> meaningless naming conventions such as Result_N, but the point is that
> I'd like to *inline* this code, but can't easily do so without
> readability suffering. Being able to chain/pipeline function calls
> would be *really* nice, so I could rewrite this in a neater style:
> 
> Fun = quite_happy/1 + not_so_bored/1 + calculate_next_thing/1 + do_thing/1, ...

Let me see you do that with more than one argument to each function.

Let's see what we have to do now:

c(F1, F2) -> fun (X) -> F2(F1(X)) end.
c(F1, F2, F3) -> fun (X) -> F3(F2(F1(X))) end.
c(F1, F2, F3, F4) -> fun (X) -> F4(F3(F2(F1(X)))) end.

doodle(X) ->
    F = c(fun do_thing/1,
          fun calculate_next_thing/1,
          fun get_bored_of_typing_result_x,
          fun start_getting_annoyed_with_lazy_programmers/1),
    {ok, F(X)}.

Nope, not hard.  Trivial, in fact.
> 
> I can't understand why the parser requires me to type "fun M:F/A" when
> the "/" character clearly indicates that I'm referencing a function in
> a special way.

The question is, in a language without static types, how do you
tell the difference between an intentional foo/2 meaning a function
and an erroneous foo/2 that should have been Foo/2, with Foo a
variable bound to a number.

What _I_ don't understand is why fun F/2 is not allowed with F a
variable.  

> And if the compiler can recognise a reference to a
> function (local or otherwise), then why not have it recognise "+" as
> an operator for chaining function calls?

Well, I've done enough mathematics to be driven screaming with rage
up the nearest handy wall at any use of "+" for an operation that is
not commutative.  The operation you want here is often called ";" in
denotational semantics.  It's (F;G) X = G(F(X)).

Again, in general the compiler *CANNOT* recognise a reference to a
function.  Haskell and SML manage that by means of type checking,
and *they* don't overload function composition with any other
operation.

> 
> Anyways - I think this work is interesting and hope that it sparks a
> good conversation about how we can potentially improve support for
> function composition and (potentially) partial application in Erlang.

The Sackman proposal does NOTHING to help with function composition,
and there are, as I've shown, much better ways to do partial application.





More information about the erlang-questions mailing list