Hacker News new | past | comments | ask | show | jobs | submit login

> I'm not sure people will want to move away from it for most development.

    x = 1 + '2' //12
    x = 1 - '2' //-1
Oh yes, yes we do.



I've been writing JavaScript in a professional context for years, and I have never added an integer to a string. I have never debugged code accidentally doing so, and I've never seen functions written that casually swap between such different return types.

Yet here we are, over and over, reading the obligatory complaint in every JS thread about its type coercion and corner cases. Who are all these programmers who can't go a day without tripping over and subtracting booleans from objects, or multiplying arrays by strings, or dividing functions by nulls? Do they suffer this affliction in other languages too? Do they divide by error codes and subtract function pointers from charcodes?


I mean, I agree with you but that's not even that bad of an example. I would expect many languages to do something similar. Something like this might be a better example:

    "\t\r\n" == 0                 → true
    "\t\r\n 16 \t\r\n" == 16      → true
    "\t\r\n 16 \t\r\n" == "16"    → false


No, I don't think most languages do something similar.


> I would expect many languages to do something similar.

Of the common dynamic languages I can think of, the only ones that do something similar are Perl/Perl6 and PHP, and even they are still saner than JS.

    $ $((1+\2))
      bash: 1+\2: syntax error: operand expected (error token is "\2")
    $ $((1-\2))
      bash: 1-\2: syntax error: operand expected (error token is "\2")
    $ ruby -e"1+'2'"
      -e:1:in `+': String can't be coerced into Integer (TypeError)
      from -e:1:in `<main>'
    $ ruby -e"1-'2'"
      -e:1:in `-': String can't be coerced into Integer (TypeError)
      from -e:1:in `<main>'
    $ python3 -c"1+'2'"
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
      TypeError: unsupported operand type(s) for +: 'int' and 'str'
    $ python3 -c"1-'2'"
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
      TypeError: unsupported operand type(s) for -: 'int' and 'str'
        >>> exit()
    $ python2 -c"1+'2'"
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
      TypeError: unsupported operand type(s) for +: 'int' and 'str'
    $ python2 -c"1-'2'"
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
      TypeError: unsupported operand type(s) for -: 'int' and 'str'
    $ perl -e'print 1+"2"."\n"'
      3
    $ perl -e'print 1-"2"."\n"'
      -1
    $ perl6 -e"say 1+'2'"          
      3
    $ perl6 -e"say 1-'2'"
      -1
    $ php -r'echo 1+"2","\n";'
      3
    $ php -r'echo 1-"2","\n";'
      -1
    $ racket -e '(+ 1 "2")'
      +: contract violation
        expected: number?
        given: "2"
        argument position: 2nd
        other arguments...:
         1
    $ racket -e '(- 1 "2")'
      -: contract violation
        expected: number?
        given: "2"
        argument position: 2nd
        other arguments...:
         1
    $ sbcl
      This is SBCL 1.3.18, an implementation of ANSI Common Lisp.
      More information about SBCL is available at <http://www.sbcl.org/>.

      SBCL is free software, provided as is, with absolutely no warranty.
      It is mostly in the public domain; some portions are provided under
      BSD-style licenses.  See the CREDITS and COPYING files in the
      distribution for more information.
      * (+ 1 "2")

      debugger invoked on a TYPE-ERROR in thread
      #<THREAD "main thread" RUNNING {1001F0EC33}>:
        The value
          "2"
        is not of type
          NUMBER
        when binding SB-KERNEL::Y

      Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

      restarts (invokable by number or by possibly-abbreviated name):
        0: [ABORT] Exit debugger, returning to top level.

      (SB-KERNEL:TWO-ARG-+ 1 "2") [external]
      0] 0

      * (- 1 "2")

      debugger invoked on a TYPE-ERROR in thread
      #<THREAD "main thread" RUNNING {1001F0EC33}>:
        The value
          "2"
        is not of type
          NUMBER
        when binding SB-KERNEL::Y

      Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

      restarts (invokable by number or by possibly-abbreviated name):
        0: [ABORT] Exit debugger, returning to top level.

      (SB-KERNEL:TWO-ARG-- 1 "2") [external]
      0] 0

      * (exit)
    $ erl
      Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

      Eshell V8.3  (abort with ^G)
      1> 1+"2".
      ** exception error: an error occurred when evaluating an arithmetic expression
           in operator  +/2
              called as 1 + "2"
      2> 1-"2".
      ** exception error: an error occurred when evaluating an arithmetic expression
           in operator  -/2
              called as 1 - "2"
      3> halt().
    $ elixir -e '1+"2"'
      ** (ArithmeticError) bad argument in arithmetic expression
          :erlang.+(1, "2")
          (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
          (elixir) lib/code.ex:170: Code.eval_string/3

    $ elixir -e '1-"2"'
      ** (ArithmeticError) bad argument in arithmetic expression
          :erlang.-(1, "2")
          (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
          (elixir) lib/code.ex:170: Code.eval_string/3


In the awk macro of TXR Lisp, I wanted to provide some of the convenience of the "duck typing" in Awk, whereby a string which looks like a number can be used like one. Instead of supporting such a thing directly (which I consider a computer science flunk job in language design) I came up with a field conversion facility that has a succinct syntax: clear and unobtrusive.

http://www.nongnu.org/txr/txr-manpage.html#N-018CCE37

This demonstrates some of it:

  1> (awk (t (fconv i) (prinl f)))
  1 2 3 4 5
  (1 "2" "3" "4" "5")
  nil
  2> (awk (t (fconv i : r) (prinl f)))
  1 2 3 4 5
  (1 2.0 3.0 4.0 5.0)
  nil
  3> (awk (t (fconv : i : r) (prinl f)))
  1 2 3 4 5
  (1 2 3 4 5.0)
  nil
  4> (awk (t (fconv i : r : i) (prinl f)))
  1 2 3 4 5
  (1 2.0 3.0 4.0 5)
There are more conversion specifiers than just r and i. There can be any number in a row before the first colon, in the middle, or after the last colon. Conversions are matched with fields and applied. If a colon occurs, conversions after the colon are repeatedly applied to the remaining fields in cyclic repetition. If two colons occur, conversions after the second colon are applied to the trailing fields. There is intelligent prioritizing when these rules overlap.

There are conversions with a z suffix which treat junk as zero:

  8> (awk (t (fconv : iz) (prinl f)))
  a b c 1 2 3
  (0 0 0 1 2 3)
Without this, you get nil which will blow up if used as a number -- but you can test for it and handle the situation rather than getting a silent zero:

  9> (awk (t (fconv : i) (prinl f)))
  a b c 1 2 3
  (nil nil nil 1 2 3)
Basically the whole "anything can be used as a number, and junk makes zero" nonsense can be avoided with only a small reduction in code golfing. Textual duck typing is a wrongheaded solution for an ergonomic problem, which sacrifices correctness and clarify of data representation for the sake of absolutely minimizing the program character count.


That is really for convenience, you can still be explicit:

  if(foo === 1 || foo === "1" || foo === true) { ...




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: