5

I'm able to write prefix boolean expressions like erlang:'and'(true, false), but not the corresponding andalso or orelse expressions. Why?

When I look at the core output, it looks like andalso and orelse are just macros - for example:

a(A,B) -> A and B.

translates to the core

'a'/2 =
    %% Line 4
    fun (_cor1,_cor0) ->
        call 'erlang':'and'
            (_cor1, _cor0)

but andalso goes from

b(A,B) -> A andalso B.

to

'b'/2 =
    %% Line 6
    fun (_cor1,_cor0) ->
        ( case _cor1 of
            ( <( 'true'
                 -| ['compiler_generated'] )> when 'true' ->
                  _cor0
              -| ['compiler_generated'] )
            ( <( 'false'
                 -| ['compiler_generated'] )> when 'true' ->
                  'false'
              -| ['compiler_generated'] )
            ( <_cor2> when 'true' ->
                  ( call ( 'erlang'
                           -| ['compiler_generated'] ):( 'error'
                                                         -| ['compiler_generated'] )
                        (( {( 'badarg'
                              -| ['compiler_generated'] ),_cor2}
                           -| ['compiler_generated'] ))
                    -| ['compiler_generated'] )
              -| ['compiler_generated'] )
          end
          -| ['compiler_generated'] )

It looks like it's implemented this way to preserve laziness, but it wouldn't have to be in this step -- e.g. there could still be a call 'erlang':'andalso' line, which is translated later.

Is it just an oversight that erlang:'andalso'(A,B) isn't equivalent to A andalso B, or does some kind of "premature expansion" make that difficult?

amindfv
  • 8,438
  • 5
  • 36
  • 58

1 Answers1

8

The main reason is that calls to BIFs behave in the same way as calls to "normal" functions in that they are strict in their arguments, all the arguments are evaluated before the BIF/function is called. The distinguishing thing with andalso and orelse is that they don't evaluate all their arguments but only the first argument. Then depending on what value of the first argument they may or may not evaluate the second. This means that even if they were BIFS they would have to be specially treated by the compiler anyway so there is very little point in making them BIFs.

Also the expansion is very straightforward so there would be little gain in doing as a they specially handled BIF.

rvirding
  • 20,848
  • 2
  • 37
  • 56
  • Still, couldn't the user be allowed to write e.g. `erlang:'andalso'(true, false)`, which would be disassembled into the laziness-preserving case expression? – amindfv Jul 14 '13 at 07:54
  • @amindfv `and`, `or`, `andalso` and `orelse` are intended for using in infix form. `erlang:'and'/2` and `erlang:'or'/2` are implemented as BIFs because there have to be implemented somewhere. `andalso` and `orelse` are implemented as expansion in compiler because they have to be implemented in this way to work properly but `erlang:'andalso'/2` and `erlang:'orelse'/2` doesn't have to be implemented anywhere so they doesn't. Get rid of surplus (waste) is Erlang spirit. – Hynek -Pichi- Vychodil Jul 14 '13 at 11:39
  • @amindfv Yes, if you had the BIF calls `erlang:'andalso'/2` and `erlang:''orelse/2` then the compiler would have to special case them and expand them to much the same code as you get today. So, yes, KISS rules. – rvirding Jul 14 '13 at 15:19
  • Too bad; I'm not a fan of infix non-operators – amindfv Jul 14 '13 at 16:09
  • @amindfv They are infix operators, they are just implemented in Erlang and not in C. And as I have tried to show they are easy to implement in Erlang and much more difficult to implement in C. – rvirding Jul 15 '13 at 10:36
  • By "operators" I meant functions named with symbols. That may only be a Haskell use of the word, though – amindfv Jul 17 '13 at 00:05