Hacker News new | past | comments | ask | show | jobs | submit login
Stupid Languages (nedbatchelder.com)
99 points by decklin on Jan 26, 2013 | hide | past | favorite | 56 comments



In JavaScript, every function is varadic. The problem is one of contract, not language. Passing varadic functions to higher-order functions without checking whether the types are compatible will cause a problem in every such language.

The problem is parseInt, not map. Consider:

   // using lodash.js (http://lodash.com/)
   
   _.mixin({ args: function () { return _.toArray(arguments); } })
  
   ['10','10','10','10'].map(_.compose(parseInt, _.first, _.args)); // #=> [10,10,10,10]


It's nice to use functional concepts and all, but in practice a simple anonymous function to trim the arguments is clearer and close to zero overhead:

    ['10','10','10','10'].map(function(n){ return parseInt(n, 10) })
That's one reason to like the conciseness of CoffeeScript, takes the urge to build compositional towers away:

    ['10','10','10','10'].map (n) -> parseInt(n, 10)
Which one would you rather find in your codebase?


Yes, you can use anonymous functions, and you probably would want to do that in practice. However, I was trying to illustrate the point that JavaScript functions are varadic and function composition was an easy way to drive that point home.

EDIT: Also, you raise a really good point in your example which is that when using parseInt you should always specify the base. If I were to modify my example to take that into account it'd get more messy.


I've been using a pair of functions applyRight/Left that fit this case nicely:

     ['10','10','10','10'].map(applyRight(null, parseInt, [10], 1)
The signature is [context, fn, args, slice] where `slice` is the length of arguments to keep from invocation. It's very rare that something warrants using it vs an anonymous function though.


That's interesting, I like how `slice` lets you concat sequences of arguments, but worry about building functions which have too many convenience features. Thought experiment: what if I wanted to pass every other argument?


Here's what I use:

  # Adapted from http://autotelicum.github.com/Smooth-CoffeeScript/literate/partial.html
  F.partial = (func, a...) -> (b...) ->
    func (for arg in a then arg ?= b.shift())..., b...
For example, do `parseInt10 = F.partial(parseInt, undefined, 10)`, then `[].map(parseInt10)` does the right thing.


I imagine you could pass a function

   function(arg, i){ return i%2 }
as the `slice` parameter, but that's going into crazy territory.


Neither.

But for the sake of argument, I'd rather see

  ['10','10','10','10'].map(Number);


['10','10','10','10'].map(Number);

this is one is correct

['10','10','10','10'].map(String);

this one makes sense

['10','10','10','10'].map(Object);

i'd like somebody to explain me this one ?????????

Javascript is really difficult as soon as one try to do non trivial stuffs.


Object('abc') is the same as new String('abc'), Object(1) the same as new Number(1) - it just calls the .constructor


but "new Number(x)" is not equal to "Number(x)". So, it's tricky, I guess?


Number(x) performs a type conversion to number, new Number(x) constructs a primitive wrapper object Number:

  typeof Number("10")
  //=> "number"
  typeof new Number("10")
  //=> "object"
The same applies to other wrapper objects, that is, Boolean and String.


This is a problem with javascript, functions shouldn't be variadic by default.


The way in which contracts are enforced or not enforced is a language issue though.


> But it can do things the other maps can't easily, like create an array of differences between elements, or a running total, and so on. Other languages have other solutions to those problems.

Lisp has some rather elegant solutions to both of those use cases. It's misleading to say that Javascript's 'map' is more powerful just because it tries to cram two or three distinct use cases into one.

It's not like Javascript even uses the Lisp definition of 'map' (which is not what most languages use for map - they use Lisp's 'mapcar').


Seems like they included extra parameters in map since they forgot to include a fold! Running totals are perfect for fold, for differences, use zipWith.

But, JavaScript is rather functional. I've heard it described as "lisp in C's clothing". Douglas Crockford uses monads in JS (intentionally) and gave an interesting yet hard to follow introduction to JS monads not too long ago. I'd like to see the functional side of JS emphasized in the next iteration.


It's fine to say that Javascript is functional, but:

>I've heard it described as "lisp in C's clothing"

I hear this line all the time, and it always irritates me. Saying that Javascript is Lisp-like just because it's somewhat functional is like saying that it's like Java because it's "object-oriented", even though there are a number of key differences (the use of "this", the prototypical inheritance model, runtime checks vs. static checks with late binding, etc.) It's worse, because it's rather trivial to force Javascript to behave somewhat like Java, but there's no way to force Javascript to exemplify the defining characteristics of a Lisp[0].

The nature of Lisp has nothing to do with lambdas and closures, mapcars and reduces - the phrase 'Lisp in C's clothing' doesn't even make sense, because the nature of Lisp cannot exist without an s-expression-based grammar[1]. Even though Lisp is known as a functional language, the defining characteristics of Lisp have nothing to do with it being functional.

[0] Perhaps a better analogy would be comparing it to JVM languages on the basis of the grammar, even though the two are completely orthogonal - Javascript isn't intended to run on the JVM, and while you can cross-translate code between Javascript and JVM languages and fake compatibility this way, that ability has very little do do with the defining characteristics of Javascript as a language.

[1] That doesn't mean you need to have parentheses; the grammar simply needs to be homomorphic with s-expressions, which leaves a great deal of flexibility. Javascript, however, does not make the cut.


Why do people keep incorrectly insisting that JavaScript is "functional" or "Scheme-like"? It is neither.

Merely having anonymous functions does not make a language "functional". JavaScript does not encourage the use of pure functions. It does not encourage the use of recursion. It does not encourage immutability. It does not have a robust, sensible type system. It does not encourage currying.

It's pretty clear that JavaScript is inherently not a functional programming language. It goes against functional programming techniques in almost all respects, especially when it comes to JavaScript code that's out in the wild.


"JavaScript does not encourage the use of pure functions."

Neither does Lisp.

"It does not encourage the use of recursion."

Neither does Lisp. (Common Lisp has no tail recursion in the spec.)

"It does not encourage immutability."

Neither does Lisp. (Actually, ES5 does have pretty powerful primitives for immutability, Object.freeze for example.)

"It does not have a robust, sensible type system."

If you mean static typing, neither does Lisp.

"It does not encourage currying."

Neither does Lisp.


sensible type system means you cannot add string and numbers.


Functional language != ML-family language.


Yes, but we can also say that functional language != Lisp-like language, and Javascript may be functional, but it is not Lisp-like.



Doug’s talk was hard to follow because he didn’t give an adequate explanation. Not because he’s subject to some curse of the monad, but because he evidently did not understand the topic well enough.


Haskell is so different from JS that is impossible to faithfully translate a monad into JS. It is like trying translate a Haiku from Japanese to English.

Maybe if he called his thing a Jonad, and explained how it was inspired by the monad's "executable semicolon" aspect, it would have been better.


This example clearly violates the principle of least surprise, but it doesn't necessarily mean that the language it comes from is bad. Rather, the code it's used in is.


"map" is sufficiently common and standard that it's a bad name for this function, irrespective of whether this is a good or bad function to include in the base language.


with the new ES6 coming , people from python are going to feel a lot of confusion , since some new functionalities are inspired by python but are in reality different from the python implementation,like modules , generators , ... Javascript has always been like that ( thinking of "this" that is totally different from "this" of Java for instance , let's not even talk about the new class keywork which will be very confusing too , for those who believe they can just go the java way with js from now on ). But i'm quite happy with the language ,it feels like self, the revenge ...


This is more of a case of a stupid library than stupid language.

And whether it's a stupid library is even debatable. map() has gained a more popular understanding from functional programming these days but it was probably not so when this Javascript map() function was added.


It is very likely that the person who added map() to JavaScript knew about its meaning in functional languages, otherwise s/he would have called it applyToEach() or something like that.


One important characteristic of the map function is that it can be used reversibly. For example, (partial map inc) is inverted by (partial map dec). If I want to map over a collection by a function that takes the entire collection as an argument I'd rather use some function other then map to avoid confusion.


or just call a closure bound to the whole list. I think I would write the thing as a for loop though, it feels so un map like


Alternatively, you could partially apply the entire list to the function.


Hmm? What is the reverse of (partial map constantFunctionReturnsZero)?

A fold/reduce is just as sometimes-reversible, when paired with an appropriate unfold. (example: multiplication and factorization)

I think you mean that map is self-composable, in the sense that the map function distributes over function-composition, in the same way that (in first-order functions) multiplication distributes over addition.


> What is the reverse of (partial map constantFunctionReturnsZero)?

The function (constantly 0) is implemented as a place in order to maintain reversibility semantics:

  ((constantly 0) 10) ;-> (0 10)
  ((constantly 0) 50) ;-> (0 50)
The information lost by calling the constant function is pushed to the end of the list so that it can be called reversibly. This can be applied on an entire list:

  (map (constantly 0) [1 2 3]) ;-> ([0 0 0] [1 2 3])
> A fold/reduce is just as sometimes-reversible

The reduce function is not reversible, but the reductions function when called with a group is:

  (reductions + [1 2 3 4]) ;-> [1 3 6 10]
The reduce value is the last element of the list so it can be retrieved by the last place:

  (last [1 3 6 10]) ;-> (10 [1 3 6])
> I think you mean that map is self-composable

The preservation of information is more important to me then composition.


To me, the bigger issue is that the unused 3rd argument is silently ignored without error or warning.


Not at all. Some other languages work this way, and in the right context (best-effort systems, not mission critical ones), it can be a boon to backward compatibility and other ease of use factors.

In Python for example you can explicitly declare variadic functions to ignore extra arguments. But when it's the default behavior you can for example change the official signature of a callback interface without breaking compatibility. It can be pretty liberating to work in a best-effort language...though perhaps it makes testing a bit more important.


Taking this reasoning to its absurd conclusion, why have signatures at all? Every function could just have an implicit args array.


Hello from Perl!


That's how JS works. You can pass as many extra parameters as you want (they get discarded) and you can also omit as many parameters as you want (it's like passing undefined).

The array-like `arguments` object will always reflect what was actually passed to the function though.


My favorite thing about JavaScript... must be a matter to personal taste.


> They want to know if function arguments are call by value or call by reference (neither).

My knowledge of Python is limited, but I was under the impression that it is call by value just like everything else (where Python's notion of value is "pointer to something"). Am I missing something?


You have it right in that it's call by value. But HN already had a fight over that a while ago: http://news.ycombinator.com/item?id=4783229 (That I apparently won. :P)


You essentially changed the definition of "value" to mean what is just called "name" in Python.

Names refer to objects. Names are introduced by name binding operations such as `=`, `import`, `def`.

  def f(a, b):  # a, b - local variables
       a = [1]  # doesn't change  x list; a refers to the new list hence forth
       b.append(2)  # change y list; b refers to the same list as y

  x, y = [], []
  f(x, y)
  print x, y  # [], [2]
Note: a "win" doesn't change the truth. It just means that nobody cared enough to participate further in the hostile discussion.


I see, people were mistaking the different nature of values in C and Python for a difference in calling semantics.


The answer -- regardless of who thinks they can "win" the argument -- is "this is not a useful question to ask, because people will draw incorrect conclusions, based on assumptions from other languages, from whichever answer is given".

Similarly, "pointer to something" is not a useful way to think about Python, because people with a background in C will start making assumptions about what "pointer" means. You might think that assigning to that "pointer" changes what's stored in the memory it points to, but really all it does is re-bind a name within the (local, which does not affect global) scope, leaving the "memory" unchanged.

So rather than ask which misleading-analogy-to-another-language Python uses, just ask how Python works.


I'm not sure I understand. Function calls work the same way in every call by value language: the binding of an argument to a value is the same as the binding of a variable or the assignment of part of a data structure. That's a pretty clear and simple thing to want to say, and "call by value" is the way to say it.

Since this is exactly the behaviour of Python's function calls, how is describing Python as call by value "not useful"? And where's this "misleading analogy to another language", given the concept is clearly explained without reference to any particular language?

I'm prepared to accept that people become confused over this, but I reject the notion that it is not a useful question.


People ask these questions because they're used to the way other languages work, and want to have an analogy to help them understand how Python works.

Except this isn't useful, because Python doesn't work the way those other languages do. They'll run into cases where their mental model of what "pass-by-value" or "pass-by-reference" means does not match up to Python's actual workings, and then they'll spend endless time arguing about which model Python uses when the answer is really "neither, stop trying to shoehorn one of those models onto a language that doesn't fit".


I think one of the reasons for the third argument (the array itself) is to permit chaining. Consider this:

    var publishedPosts = posts.filter(function(post){
        return post.published
    })

    var intervals = publishedPosts.map(function(post, i){
        var next = publishedPosts[i+1]
        return post.date - (next && next.date || 0)
    })
With the array reference you don't need an intermediate var:

    var intervals = posts.filter(function(post){
        return post.published
    }).map(function(post, i, arr){
        var next = arr[i+1]
        return post.date - (next && next.date || 0)
    })


I think in that case it would be better to make it variadic on input arrays, like map(makeInterval, array, array.tail())


For another quick 'WAT' talk, see Gary Bernhardt from CodeMash 2012.

https://www.destroyallsoftware.com/talks/wat


Base 1 does make sense!

0 = 0

00 = 1

000 = 2

0000 = 3

etc


What you've done is define your own number system, I do not believe you are representing what "base 1" might be.

If a base means, you can write numbers of this form:

abc

Where the value is:

aB^2 + bB^1 + cB^0

and you can only have B symbols, then your one symbol for base 1 would need to be 1, still useless, but not completely:

1 = 11^0 = 1 11 = 11^1 + 11^0 = 2 ...

Note that you cannot represent 0 in base 1 with this method.

If you choose 0 as your one symbol for base 1, then the only number you can represent is 0. I assert this is even more useless than selecting 1 as the symbol.

0 = 01^0 = 0 00 = 01^1 + 01^0 = 0 ...

As far as I can tell, the example was trying to interpret "10" as base 1. 10 has two different symbols which truly does not make sense in base 1.

I assert that base 1 is not a valid base, because you cannot represent all integers in it.

Additionally, the "unary point" or whatever it would be called, would serve no purpose:

1.1 = 11^0 + 1*1^-1 = 2

I'm not sure how that might disqualify something for a "base" but it certainly doesn't help :) Probably the strongest argument is the inability to represent 0.


I had the same reaction as you had, but apparently, OP is correct:

https://en.wikipedia.org/wiki/Base_1

though you are right that it's not the same as base N, N>1. Also, Wikipedia uses |||| to represent 4 instead of 0000. It makes more sense.


You can represent 0 by absence.

     : 0
  o  : 1
  oo : 2
  ooo: 3
Of course, this is not a practical notation, since 0 would hard to distinguish from actual absence, but it would be viable in some situations: a table of numbers say, where you know that no cells are empty. So theoretically, it is possible to represent 0 in base 1, regardless of its obvious impracticalities.


That's using a different notation than our more familiar number systems, though.

If we were treating it like base 2 (or 10 or 8 or 16), we would evaluate 0000 as 1^30+1^20+1^10+1^00 = 0. A number system where the only number you can represent is 0 is a bit less useful.




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

Search: