[erlang-questions] I think I wish I could write case Any of whatever -> _ end.

Joe Armstrong erlang@REDACTED
Tue May 18 11:06:12 CEST 2010


On Tue, May 18, 2010 at 5:25 AM, Eric Newhuis (personal)
<enewhuis@REDACTED> wrote:
> The specific case I've had a number of times is when writing short filter functions that either pass through or modify something.
>
> I'd find myself almost forced to write the following:
>
> case find(...) of
>        error ->
>                {ok, Default};
>        {ok, Value} ->
>                {ok, Value}
> end.
>
> And the reconstruction of a small tuple bugged me.  Your suggestion shortens this to something arguably easier to type.
>
> case X = find(...) of
>        error ->
>                {ok, Default};
>        {ok, Value} ->
>                X
> end.


I'd write:

case find(...) of
     error -> {ok, Default};
     X -> X
 end.

(( assuming I know that find returns error | {ok, Value} ))

Save the compiler some work :-)


/Joe




>
> ...without the apparent overhead of reconstruction.  (Is it a moot point due to a clever compiler?)
>
> But I guess I am still concerned about suggesting this as a general programming idiom because it introduces a temporary variable.  And as such one would need to study pre- and post-logic to avoid accidental use of the same variable name to name something different.
>
> I'd not have this concern if Erlang had a LET construct:
>
> let X = find(...) in
>        case X of
>                error ->
>                        {ok, Default};
>                {ok, Value} ->
>                        X
>        end.
>
> Variable scope is important.  Of course...absurdly...
>
> (fun() ->
>        case X=find(...) of
>                error ->
>                        {ok, Default1};
>                {ok, Value} ->
>                        X
>        end
> end)(),
> (fun() ->
>        case X=obviously_different_fetch(...) of
>                error ->
>                        {ok, Default2};
>                {ok, Value} ->
>                        X
>        end
> end)().
>
> But that is ridiculous.
>
> So apparently my idea isn't so great.  And I also don't have a satisfying temporary-free and reconstruction-free solution.
>
>
> On May 17, 2010, at 6:09 PM, Richard O'Keefe wrote:
>
>>
>> On May 18, 2010, at 2:00 AM, Eric Newhuis (personal) wrote:
>>
>>> For the record, I might still disagree, so far.  I'm not sure.  Simply for argument's sake...
>>>
>>> The context in which this might be maximally useful is the following.
>>>
>>> case some_module:some_function(...) of
>>>      {some, pattern} -> _;
>>>      {some, other, pattern} -> _;
>>>      _ -> whatever
>>> end.
>>
>> What is wrong with
>>
>>    case X = some_module:some_function(...)
>>      of {some, pattern} -> X
>>       ; {some, other, pattern} -> X
>>       ; _ -> whatever
>>    end
>>>
>>> Note that I belong to the school of philosophy that suggests that the number of temporary variables should be minimized.
>>
>> By using the wild card, you have *NOT* minimised the number of temporary
>> variables.  All you have done is to carefully deprive your reader of any
>> clues as to what it is about.
>>
>>
>>> I don't understand why the above would be called wild-card abuse.
>>
>> Because the whole *point* of the wild-card is that each and
>> every occurrence of "_" should represent a DIFFERENT variable.
>> That's what it means in Prolog, Mercury, Strand88, Parlog, GHC,
>> ML, Haskell, Clean, ...  Every time, a different variable.
>> But you are relying on the "_" in each arm of the case being
>> the *SAME* variable.
>>
>> You are also creating great confusion.
>> Suppose I have
>>
>>       case foo()
>>         of {ping,_} -> _
>>          ; {_,pong} -> _
>>       end
>>
>> The wild cards *following* the arrows are the *same* variable;
>> what about the wild cards inside the patterns?  If not, why not?
>>
>> What if I write
>>       case foo() of _ -> case bar() of _ -> _ end end
>> Does this mean the same as
>>       case foo() of X -> case bar() of X -> X end end
>> or    case foo() of X -> case bar() of Y -> X end end
>> or    case foo() of X -> case bar() of Y -> Y end end
>> or what?
>>
>> There's another point.  In Erlang as it stands, _every_
>> variable _without exception_ must be visibly present at
>> the point where it is bound.  (Wild cards are no exceptions
>> to this rule).  You are introducing a new reading of "_"
>> that violates this rule.  If you want to think of the
>> variable as bound in the pattern, write
>>       case Expr
>>         of X = Pattern when Guard -> X
>> If you want to think of the variable as bound in the head,
>> write
>>       case X = Expr
>>         of Pattern when Guard -> X
>> In either case, the variable X is *visibly* bound.
>>
>>> It is clear from context that the wild-card represents something other than matching.
>>
>> Yes, but we already *have* something we can use for this purpose.
>> Ordinary variable names.
>>
>>
>>> We've seen this idea before.  There are those grammars that expose variables whose value is whatever matched.
>>
>> I have no idea what you are referring to.  Can you explain?
>>
>> "Exposing variables" is different from "invisibly binding anonymous
>> variables".
>>
>>>
>>> I suppose the following is where this great idea of mine might break down.
>>>
>>> case some_module:some_function(...) of
>>>      {some, pattern} ->
>>>              {encapsulated, _};
>>>      {some, other, pattern} ->
>>>              {another, _, encapsulation}
>>> end.
>>>
>>> Although I still don't have a problem with that.
>>
>> You may not.  I do.
>>
>>> From context I know that the right hand side of the arrow isn't pattern matching.
>>
>> You as author may; your reader WILL have to work harder in reading
>> to find out what the context *is*, especially if (as is often the
>> case) the arrow is on the previous screen.
>>
>> This would be better as
>>
>>       case X = some_module:some_function(...)
>>         of {some, pattern} ->
>>                {encapsulated, X}
>>          ; {some, other, pattern} ->
>>                {another, X, encapsulation}
>>       end
>>
>> There is not one of your examples that would not be massively improved
>> by introducing a named variable, even a literal X.
>>>
>>> I guess where readability might break down is in nesting:
>>>
>>> case some_module:some_function(...) of
>>>      {some, _, pattern} -> % _1
>>>              case _ of ->  % _2
>>>                      {some, great, pattern} ->
>>>                              not_so_bad;
>>>                      _ -> % _3
>>>                              {_, Kind, _} = _, % _4, _5, _6
>>>                              Kind
>>>              end
>>> end.
>>>
>>> Although I can still read the above once I learn that underscore ('_') is context sensitive.
>>>
>>> _1 :: any()
>>> _2 :: {some, any(), pattern}
>>> _3 :: {some, any(), pattern}, not {some, great, pattern}
>>> _4 :: some
>>> _5 :: pattern
>>> _6 :: _3
>>
>> If I want to play at silly puzzles I do the Sudoku problem in the
>> newspaper.  If I'm reading a program, I do *NOT* enjoy pointless
>> stumbling-blocks placed in my path to understanding.
>>
>> Let's rewrite your example.
>>
>>       case some_module:some_function(...)
>>         of {some,Kind,pattern} ->
>>                case Kind
>>                   of great -> not_so_bad
>>                     _     -> Kind
>>                end
>>       end
>>
>> There is now *no* puzzle-solving for the reader.
>>
>> Let me raise my usual refrain:
>>       let's have a REAL example.
>>
>>
>
>
> ________________________________________________________________
> erlang-questions (at) erlang.org mailing list.
> See http://www.erlang.org/faq.html
> To unsubscribe; mailto:erlang-questions-unsubscribe@REDACTED
>
>


More information about the erlang-questions mailing list