Er, actually
x = 2
x = "foo"
[y] = [3]
[y] = ["bar"]
Sorry, I didn't re-read the code before translating.HN user
Er, actually
x = 2
x = "foo"
[y] = [3]
[y] = ["bar"]
Sorry, I didn't re-read the code before translating.I'm not sure what you find incomprehensible about the first example. The syntax is pretty standard. The only exotic thing is `$`, which is basically just like putting brackets around the rest of the line. Here's the first example roughly translated to Python:
def main():
x = 2
[x] = ["foo"]
y = 3
[y] = ["bar"]
print(x + y)
Seems about the same level of comprehensibility to me. Is there anything in particular you find difficult to understand?The second example is expanded out and not how a person would normally write it, but if you're familiar with the basic concepts it's using, it shows why it works very clearly; think of it like assembler.
It works the same way in Haskell, eg
main = do
let x = 2
let x = "foo"
y <- pure 3
y <- pure "bar"
putStrLn $ x ++ y
which is really the same as main =
let x = 2
in let x = "foo"
in pure 3 >>= \y ->
pure "bar" >>= \y ->
putStrLn $ x ++ y
So it works pretty naturally where each assignment is kind of like a new scope. If the type system is good, I don't think it really causes issues.Surely not in Canada?
I can hear them and tell that they're different accents, but I don't really distinguish them, I would call them both "British" and I wouldn't know which one's more posh.
Usually that effort should go toward naming functions rather than their results, though, and if the functions have good names, the results don't need them. In this example, `other_function` could have been named `get_user_data`, `new_function` could have been called `extract_user_details`, whatever.
Once you have good function names, which you should generally be spending a lot more effort on than good local variable names, you won't find any value in adding variables like `var foo = get_foo()`.
Works in Ruby:
irb(main):001:1/ puts(%r
irb(main):002:1* a+
irb(main):003:0> .match? %q aaa )
trueYou can also ssh in via Emacs with Tramp, and use your highly customised Emacs to edit remotely.
Unsigned integers are still specified to wrap.
Clojure has cons, car, cdr which you can use in the classic Lisp sense. The only thing you CAN'T do is have a dotted pair or dotted list in the last cons cell.
So, it doesn't have them in the classic Lisp sense. Conses are just pairs. Using them as such isn't exotic. (cons 1 2) being an error in Clojure isn't a minor thing, it's very unique compared to other Lisps. It has a very different definition of cons.
Yeah, it's definitely a taste thing, but as the first style isn't used in Lisp, comparing its readability doesn't really make sense. Indentation is important to reading Lisp. Indenting it in an odd way makes it harder to parse. It's a bit like giving
def
foo(x)
bar(x)
end
as an example of Ruby syntax being overly homogeneous.That isn't how that defun would normally be formatted, though. Lisp knows that the 'body' part starts with the 3rd argument (the same way Clojure knows it starts with the second), so it indents the lambda list farther right (if it doesn't fit on the first line, otherwise it would go there).
For my eyes, [] aren't distinct enough from () to make the second style preferable. I'd rather have indentation to set it apart.
Could you expand on how Forth differs from Lisp? I don't know Forth.
SICP called it design by wishful thinking; I always liked that.
You're mixing your "playing by JS' rules" point with just general Elm features. Faster compilation speed, faster executables, and smaller executables are not JS' rules, they're just generally desirable things to have. "Faster than Webpack" and "no runtime exceptions" are certainly not the JS world's rules (does Purescript have runtime exceptions?). That a typical JS developer can get up to speed quicker in Elm seems very nebulous to me; why? Could you elaborate more on what you're talking about? How is Purescript not playing by JS rules, other than being (I assume) slower than Elm?
If you have a recursive data structure, using tail recursion over that structure is significantly more straightforward than writing iteratively. I actually wrote a comment about this recently: [0].
That example isn't tail recursive, though. The Python version is more difficult to read because you're using a manual stack instead of relying on the built-in one. An iterative algorithm, whether written using lexical recursion or a for loop, would entirely remove the use of a stack, not just hide the stack in your language implementation. Converting an iterative algorithm between the two forms is a simple syntax transformation, and doesn't introduce bookkeeping like that. Converting a body recursive function to iterate with an in-language stack introduces a lot of noise even if you use tail recursion to do the iteration.
The tail recursive Haskell version of your Python isn't much better:
sumTree :: BTree -> Int
sumTree t = sumTree' [t] 0
where sumTree' [] total =
total
sumTree' (Leaf v : rest) total =
sumTree' rest (v + total)
sumTree' (Branch v l r : rest) total =
sumTree' (l : r : rest) (v + total)I believe they're still object pointers in CPython, they're just ordinary singletons; in other implementations, they aren't even singletons. Python's object model doesn't have primitives. Adding special rules around them would significantly complicate the language as well as limit the implementors' ability to fiddle with the ranges of pre-allocated instances, for minimal benefit.
I don't think that's a great example for Python; that's just knowing what "is" is for. The fact that it works the same as == for small integers in CPython is an optimisation showing through, but only in a place where it doesn't really matter.
You still need to have a grasp on the difference between reference equality and value equality without getting into anything anyone would call tricks or implementation details (eg, after `x = []; y = []; x.append(1)`, how many elements does y have?).
Right, they describe different aspects of how types work in a language.
Static typing is not a superset of strong typing, they're on different axes. Strong vs weak typing (which I explained in the second paragraph) is about how strictly types need to match expected types before you get a type error. Static vs dynamic typing is about when you get a type error (during a static typechecking phase, or at runtime when you try to use a value as that type).
When you say the type cannot change, that's ambiguous: do you mean the type of the value a variable holds, or the type of the value itself? In C (a statically typed language), "int x" means that x will always hold an int, but you can still assign a pointer to it, it just turns into an int (weak typing). In Python (a dynamically typed language), the variable "x" wouldn't have a type (so it could hold an int at one point and a string later), but the value it holds does, and because it's strongly typed, it would throw a type error if you attempted to use it in a place where it wanted a different type (eg, `1 + "2"` does not turn 1 into a string or "2" into an int).
When people say that, they mostly just mean that it has lexical closures, I think.
Why aren't statically typed programs really just dynamically typed programs where all the types happen to be statically inferable?
Static typing means that types are figured out statically by looking at the source code, and type errors are detected then when it notices a mismatch. Dynamic typing means that types are worked out at runtime by looking at live objects when code operating on them executes.
Strong typing means that types cannot be substituted for other types. In C, you can write `int x = "one"` and the char * (address of) "one" is automatically converted to an int, or in Javascript you can write 1 + "2" and a string "1" is automatically created; depending who you're talking to, either or both of these qualify as weak typing.
They're both spectrums, and commonly confused with each other.
Not automatically coercing values is all that strong typing means. Getting a type error before you run the program is static typing. They're separate axes, and both useful to talk about in a language.
A method named "method!" means that it's a somehow "unsafe" version of the method "method". A lot of the time it means "destructive version," but if there's no non-destructive version, the destructive one won't have a ! (eg, Array#shift), and sometimes ! means something else (eg, Kernel#exit! is like Kernel#exit, but doesn't run any at_exit code).
Threads are C11, not C99.
Use tramp with Emacs and you can interact with remote machines as if they were local. Who cares if the server only has vim, you have Emacs.
The significant difference is that in Rust, like in Lisp, `if` is an expression, so if-else essentially is the ternary operator.
const x = cond1 ? a
: cond2 ? b
: c
becomes in Rust let x = if cond1 { a }
else if cond2 { b }
else { c }The part that is relatively unique to JS is where extra arguments passed to a function are silently ignored. In most every other language, the function passed to map takes 1 argument, which is also the common case in JS. But JS always passes 3 arguments to that function. I don't think it's optional parameters that people find confusing, it's the interplay between optional parameters and functions whose interface depends on functions they accept as arguments ignoring extra arguments.
It doesn't matter when you're making the edit, it matters when you're looking at the diff and skimming through a hundred identical changes for the things that are actually different.