Emacs unfortunately uses Emacs lisp, not common lisp or scheme.
Emacs unfortunately uses Emacs lisp, not common lisp or scheme.
Symbols display with friendly string-y names in a number of languages. Clojure, for example, has a symbol type.
And a number of languages display friendly strings for enumy things - Scala, Haskell, and Rust spring to mind.
The problem with strings over enums with a nice debugging display is that the string type is too wide. Strings don’t tell you what values are valid, strings don’t catch typos at compile time, and they’re murder when refactoring.
Clojure symbols are good at differentiation between symbolly things and strings, though they don’t catch typos.
The other problem the article mentions is strings over a proper struct/adt/class hierarchy is that strings don’t really have any structure to them. Concatenating strings is brittle compared to building up an AST then rendering it at the end.
Edit: autocorrect messed a few things up I didn’t catch.
You can make a pretty decent Mac and cheese using only macaroni, cheese and evaporated milk.
Or macaroni, cheese, and sodium citrate.
It’s powdered cheese sauce.
It’s mostly made using spray dryers. Basically, a spray dryer uses hot air to quickly dry a fine mist of something. It’s how they make milk powder and egg powder as well.
Baked pasta works better if you start off only parboiling the pasta so it doesn’t overcook.
American cheese is not a fancy cheese, yeah. It also isn’t great cold.
It is a pretty decent cheese sauce, though. It doesn’t taste starchy, with a muted cheese flavor like mornay does. If you put extra sharp cheddar or smoked gouda in homemade mornay or homemade American, the homemade American will be noticeably cheesier. Having made homemade mac and cheese a number of ways, I strongly prefer homemade American to any of the other recipes I know.
I only really eat store-bought American melted, such as on eggs in a breakfast sandwich, on a burger or grilled cheese.
Also, there’s plenty of decent American cheeses, like Humboldt fog, Maytag blue, or rogue river blue. American cheese is called that, but it doesn’t define American cheese making.
American cheese isn’t made of plastic in the sense of polymers, it is plastic in the sense of being easily deformed or molded.
At its most basic, American cheese is literally just cheese, water and sodium phosphate. It’s “not cheese”, but in the sense that meatloaf isn’t meat and mayonnaise isn’t eggs.
It’s not that there’s anything unnatural about water. It’s just not a remedy for anything but dehydration.
Javascript is generally considered OOP, but classes weren’t widely available till 2017.
Inheritance isn’t fundamental to OOP, and neither are interfaces. You can have a duck- typed OOP language without inheritance, although I don’t know of any off the top of my head.
Honestly, the more fundamental thing about OOP is that it’s a programming style built around objects. Sometimes OO languages are class based, or duck typing based, etc. But you’ll always have your data carrying around it’s behavior at runtime.
keeping state (data) and behavior (functions) that operate on that state, together
Importantly, that’s “together at runtime”, not in terms of code organization. One of the important things about an object is that it has dynamic dispatch. Your object is a pointer both to the data itself and to the implementation that works on that data.
There’s a similar idea that’s a bit different that you see in Haskell, Scala, and Rust - what Haskell calls type classes. Rust gives it a veneer of OO syntax, but the semantics themselves are interestingly different.
In particular, the key of type classes is keeping data and behavior separate. The language itself is responsible for automagically passing in the behavior.
So in Scala, you could do something like
def sum[A](values: List[A])(implicit numDict: Num[A]) = values.fold(numDict.+)(numDict.zero)
Or
def sum[A: Num](values: List[A]) = values.fold(_ + _)(zero)
Given a Num typeclass that encapsulates numeric operations. There’s a few important differences:
All of the items of that list have to be the same type of number - they’re all Ints or all Doubles or something
It’s a list of primitive numbers and the implementation is kept separate - no need for boxing and unboxing.
Even if that list is empty, you still have access to the implementation, so you can return a type-appropriate zero value
Generic types can conditionally implement a typeclass. For example, you can make an Eq instance for List[A] if A has an Eq instance. So you can compare List[Int] for equality, but not List[Int => Int].
Apparently my brain silently inserted “ing with” into the middle of “program functions” a half dozen times without me noticing.
It’s really not just that Rust is new and C is old. Compare Rust with Go, for example. Go is a fairly modern example of a ‘worse is better’ language.
Back in 1970s when C was invented, Lisp had been around for over a decade. C came out the same year as smalltalk, a year before ML, and 3 years before Scheme.
Rust is a very modern language, yes. There’s no way we could have had it in the 70s; many of its language features hadn’t been invented yet. But it very much depends on MIT style research languages for its basis.
I agree with you that if you use functions it’s functional.
But many people don’t really realize how that contrasts with procedures and procedural code.
Functions, here, being the key word.
Functions are pure mappings from input to output.
C is many things, but elegant really isn’t one of them.
C has always been part of the “worse is better”/New Jersey school of thinking. The ultimate goal is simplicity. Particularly simplicity of language implementation, even if that makes programs written in that language more complex or error prone. It’s historically been a very successful approach.
Rust, on the other hand, is part of “The Right Thing”/MIT approach. Simplicity is good, but it’s more important to be correct and complete even if it complicates things a bit.
I don’t really think of void* and ubiquitous nulls, for example, as the hallmark of elegance, but as pretty simple, kludgey solutions.
Rust, on the other hand, brings a lot of really elegant solutions from ML- family languages to a systems language. So you get algebraic data types, pattern matching, non-nullable references by default, closures, typeclasses, expression-oriented syntax, etc.
Just like walking doesn’t really compete, like at all, with flying in an aircraft, Functional and Object Oriented Programming are at their best when you use whichever approach makes sense for a given situation and in any reasonably complex software that means your code should be full of both.
I’m not really sure sure that’s true.
In FP languages like Haskell, you get tools like algebraic data types, typeclasses, and pattern matching.
FP is really opposed to imperative programming, while objects are opposed to algebraic data types.
You can write OO code that’s 100% fully functional, and you can write code in Haskell or rust where you barely notice you never once used an object.
Yeah, OO and FP aren’t really opposed. FP is opposed to imperative programming.
That said, most FP languages give you a slightly different set of tools to use. Algebraic data types and typeclasses are really, really nice.
Honestly, working in Haskell or rust, you don’t really miss the fact that you have to jump through hoops to get traditional OO objects. There’s just not really many cases where you need them.
In the US, at least, AI works are inherently public domain. Because copyright only applies to works with a human author.
Pure functions should be referentially transparent; you should be able to replace them with whatever value they evaluate to without changing the semantics of your code.
Throwing is referentially impure: what value do you get from calling x => throw new RuntimeException()
?
Instead, functional languages prefer to return a tagged union of the value or the error.
Emacs is a bunch older than common lisp.
One of its more idiosyncratic design decisions was using dynamic scope, rather than lexical scope. They did add in per-file lexical scope, though.
It also just doesn’t implement a lot of common lisp’s standard library.