[1]: https://www.cambridge.org/ir/universitypress/subjects/comput...
Ruby is object oriented from the ground up. Everything (and I do mean everything) is an object, and method call is conceived as passing messages to objects.
While Ruby is most often compared to Python (an Algol), they come from very different evolutionary routes, and have converged towards the same point in the ecosystem. I think of Ruby as a cuddly Alpaca compared to Python's spitting camel.
I love to point this out to OOP haters,
>>> type(42)
<class 'int'>
>>> dir(42)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'is_integer', 'numerator', 'real', 'to_bytes']- Agda, Idris, etc. are functional languages extended with complex types
- Isabelle, Lean, etc. are functional languages extended with complex types and unreadable interactive proofs
- Dafny etc. are imperative languages extended with theorems and hints
- ACL2 is a LISP with theorems and hints
Related, typeclasses are effectively logic programming on an otherwise functional/imperative language (like traits in Rust, mentioned in https://rustc-dev-guide.rust-lang.org/traits/chalk.html).
OTOH if you really want to emphasize "intended to express proofs" then surely Prolog has that covered, so Lean can be seen as half ML, half Prolog. From this view, the Curry-Howard correspondence is just an implementation detail about choosing a particular computational approach to logic.
Incidentally, this is pretty much what Algol 60 was designed for and why to this day many academic papers use it or a closely related pseudocode.
The results surprised me:
F# 100 19.17s ±0.04s
C++ 96 19.92s ±0.13s
Rust 95 20.20s ±0.38s
Kotlin 89 21.51s ±0.04s
Scala 88 21.68s ±0.04s
Kotlin-native 81 23.69s ±0.11s
Scala-native 77 24.72s ±0.03s
Nim 69 27.92s ±0.04s
Julia 63 30.54s ±0.08s
Swift 52 36.86s ±0.03s
Ocaml 47 41.10s ±0.10s
Haskell 40 47.94s ±0.06s
Chez 39 49.46s ±0.04s
Lean 10 198.63s ±1.02s
https://github.com/Syzygies/CompareI don't think these results are quite comparable because of slightly differing parallelism strategies; I'd expect the F# implementation of just spinning off threads to be more a little more performant than a Rayon parallel iterator, which presumably has some overhead. But that really just shows how hard it is to do a cross-language comparison; Rust and C++ can certainly be made faster than the F# code by carefully manipulating a ton of low-level OS concurrency primitives. This would arguably also be little misleading. Likewise Chez and Haskell have good C FFI; does that count? It's a tricky and highly qualitative analysis.
[1] FYI, one possible performance improvement with the Chez code is keeping the permutations in fxvectors and replace math operations with the fixnum-specific equivalent - this tells the compiler/interpreter that the data are guaranteed to be machine integers rather than bigints, so they aren't boxed/unboxed. I am not sure without running it myself, but there seems to be avoidable allocations in the Chez implementation. https://cisco.github.io/ChezScheme/csug/objects.html#./objec...
Given the sane C defaults that are applied by code extraction techniques, the delta really shouldn't be so great. But it's a common pitfall to torture one's own verified code in order to get it proven, and I'm also not sure how good of support there is for parallelism.
plus up and coming (actual production-ready) languages that don't fit perfectly in the 7 categories: unison, darklang, temporal dataflow, DBSP
It may feel like a little bit of cheating mentioning the above ones, as most are parallel to the regular von Neumann machine setup, but was meaning for a while to do an article with 'all ways we know how to compute (beyond von Neumann)'.
We agree on Algol, Lisp, Forth, APL, and Prolog. For ground-breaking functional language, I have SASL (St Andrews Static Language), which (just) predates ML, and for object oriented language, I have Smalltalk (which predates Self).
I also include Fortran, COBOL, SNOBOL (string processing), and Prograph (visual dataflow), which were similarly ground-breaking in different ways.
It made learning Elixir years later much easier.
We also had a course that basically summed up to programming agents to play Unreal Tournament in a language called GOAL which was based on Prolog.
For years I've wanted to use Prolog but could not figure out how. I ended up making a spellcheck to allow LLM's to iterate over and fix the dismal Papiamentu they generate.
I think from a historical perspective, describing COBOL and Fortran as part of the ALGOL family is a stretch, but I suppose it’s a good reminder that all history is reductive.
We didn't learn APL (Who is teaching the use of those custom keyboards to 100s of young students for one semester?)
The processing power of systems at the time made it clear which language classes were practically useful and usable for the time and which were not.
Prolog ran like a dog for even simple sets of logic.
We had the best internet access and pretty powerful desktop systems for the time.
I'm still curious why we didn't learn smalltalk. Could have been the difficulty of submitting and marking a system in a particular state rather than a file of code :)
One I might suggest is scripting languages, defined loosely by programming tools which dispatch high-level commands to act on data pipelines: sed, AWK, the sh family, Perl, PowerShell, Python and R as honorary members. In practice I might say SQL belongs here instead of under Prolog, but in theory of course SQL is like Prolog. Bourne shell might be the best representative, even if it's not the oldest.
AWK et al share characteristics from ALGOL and APL, but I feel they are very much their own thing. PowerShell is quite unique among modern languages.
At the same time, I don't agree with that it does not matter if one picks "Java, C#, C++, Python, or Ruby". If your goal is to do quick sort, then well, it does not.
If you want to use language for something (not only for its sake), then it makes a day and night difference. A person who wants to do 3D games and being shown Ruby or a person wanting to do exploratory data science and deep learning and being given Java are likely to get discouraged.
1) Advanced Programming Language Design by Raphael Finkel - A classic (late 90s) book comparing a whole smorgasbord of languages.
2) Design Concepts in Programming Languages by Franklyn Turbak et al. - A comprehensive (and big) book on PL design.
3) Concepts, Techniques and Models of Computer Programming by Peter Van Roy et al. - Shows how to organically add different programming paradigms to a simple core language.
Err
"Fortran: The world’s first programming language standard opened the door to modern computing"
One thing the article didn’t touch on was SmallTalk’s live visual environment. It’s not a normal source code / text language.
- Forth: you can use PFE,Gforth for ANS Forth requeriments. Or EForth if you reached high skills levels where the missing stuff can be just reimplemented.
EForth under Muxleq: https://github.com/howerj/muxleq I can provide a working config where a 90% of it would be valid across SF.
Starting Forth, ANS version: https://www.forth.com/starting-forth/
Thinking Forth, do this after finishing SF: https://thinking-forth.sourceforge.net/
Also, Forth Scientific Library. You can make it working with both GForth and PFE, just read the docs.
Full pack: https://www.taygeta.com/fsl/library/Library.tgz
Helping Forth code for GForth/PFE. If you put it under scilib/fs-util.fs, load it with:
s" scilib/fsu-util.fs" included
https://www.taygeta.com/fsl/library/fsl-util.fs- Lisp. s9fes, it will compile under any nix/Mac/BSD out there, even with MinC.
S9fes: http://www.t3x.org/s9fes/
Pick the bleeding edge version, it will compile just fine.
For Windows users: MinC, install both EXE under Windows. First, mincexe, then buildtools*exe: https://minc.commandlinerevolution.nl/english/home.html
Then get 7zip to decompress the s9fes TGZ file, cd to that directory, and run 'make'.
Run ./s9 to get the prompt, or ./s9 file.scm where file.scm it's the source code.
In order to learn Scheme, there's are two newbie recommended books before "SICP".
Pick any, CACS, SS, it doesn't matter, both will guide you before SICP, the 'big' book on Scheme:
Simply Scheme https://people.eecs.berkeley.edu/~bh/pdf/
Simply.scm file, select from ';;; simply.scm version 3.13 (8/11/98)' to '(strings-are-numbers #t)' and save it as simply.scm
https://people.eecs.berkeley.edu/~bh/ssch27/appendix-simply....
Concrete Abstractions
Book:
https://www.d.umn.edu/~tcolburn/cs1581/ConcreteAbstractions....
The SCM files needed to be (load "foo.scm") ed in the code in order to do the exercises:
https://github.com/freezoo/scheme-concabs
If you are en Emacs user, just read the Elisp intro, it will work for a different Lisp family but with similar design.
Spot the differences:
Scheme (like s9):
(define (square x)
(* x x))
We try: >(square 20)
400
Elisp/Common Lisp (as the web site shows): (defun square (x)
(* x x))
Same there: >(square 20)
400
- Ok, ML like languages:https://www.t3x.org/mlite/index.html
If you follow the instructions on compiling s9, mlite it's similar with MinC for Windows. If you are a Unix/Linux/Mac user, you already know how to do that.
You got the whole docs in the TGZ file, and the web.
in jq, the comma separates expressions, which independently yield values. a span of such expressions is called a 'filter', since they are always run by passing values from the prior filter into them (with the initial values sourcing from json objects on stdin, or an implicit null if you pass -n to the program).
$ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; x, y '
"a"
"b"
"c"
1
2
3
$ jq -c '. + 10, . + 20' <<< '1 2 3'
11
21
12
22
13
23
brackets collect values yielded inside of them. $ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; [x,y] '
["a","b","c",1,2,3]
if you have a complex object that includes multiple expressions yielding multiple values, construction will permute over them. $ jq -nc ' def x: "a", "b", "c" ; def y: 1, 2, 3 ; {"foo": x, "bar": y} '
{"foo":"a","bar":1}
{"foo":"a","bar":2}
{"foo":"a","bar":3}
{"foo":"b","bar":1}
{"foo":"b","bar":2}
{"foo":"b","bar":3}
{"foo":"c","bar":1}
{"foo":"c","bar":2}
{"foo":"c","bar":3}
the pipe operator `|` runs the next filter with each value yielded by the prior, that value represented by the current value operator `.`. $ jq -nc ' 1,2,3 | 10 + . '
11
12
13
$ jq -nc ' 1,2,3 | (10 + .) * . '
11
24
39
binding variables in the language is similarly done for each value their source yields $ jq -nc ' (1,2,3) as $A | $A + $A '
2
4
6
functions in the language are neat because you can choose to accept arguments as either early bound values, or as thunks, with the former prefixed with a $.for example, this runs `. + 100` parameters context, with `.` as the 10,20,30 passed to it:
$ jq -nc ' def f($t): 1,2,3|$t ; 10,20,30|f(. + 100) '
110
110
110
120
120
120
130
130
130
where this runs `. + 100` in the context of its use inside the function, instead receiving 1,2,3: $ jq -nc ' def f(t): 1,2,3|t ; 10,20,30|f(. + 100) '
101
102
103
101
102
103
101
102
103
so you could define map taking a current-value array and applying an expression to each entry like so: $ jq -nc ' def m(todo): [.[]|todo] ; [1,2,3]|m(. * 10) '
[10,20,30]
it's a fun little language for some quick data munging, but the semantics themselves are a decent reason to learn it.for example, I would say what i was taught, that lisp is lists made out of cons cells, which contain references to either other lists/cons cells or to atoms. lisp doesn't "have" parentheses any more than lisp has the rest of the ASCII table, but parentheses were chosen as a convenient way to write list structure down in ascii form because parentheses nest in exactly the way lists do and lists are the important thing.
then there never needs to be another discussion of parentheses and how you feel about them except in a sub-discussion if anybody has any bright ideas about alternative ways to convert list structure to ascii on paper. you disliking parentheses does not mean you dislike lisp; it just means you don't understand lisp, like for instance you think lisp has something to do with ascii.
similar ideas apply to C, wherein the braces and semicolons do not matter, what matters is datatypes that correspond to von neumann architectures including especially pointers, and where binary representation is easily exposed (with a syntax that does not matter but for the record is &, |, ~, ^, <<, or >>. if you hate those choices for operator syntax you could easily convert your own C to use parenthetical lists of CamelCase names of LeftAddRight or whatever floats your boat instead of a + sign), just keep in the front of your mind how data is represented internally and how it's mapped to the syntax you like, and whatever list structure you choose to order the lines of code)
Ur-ness is not interesting, just as the city of Ur was no doubt a lot less interesting than the city of Rome. Study Algol only to learn about call-by-name, then forget it.
I could go on, but, like the ripples from a gold coin thrown in the bay, the effect will soon dissipate as if I had never written anything at all.