4 ms·
Well, actually, let's explore the metaphor. Here's how I compose music. First, I use a highly-flexible prototyping instrument to quickly iterate on melodic ide
by DonbunEf7 9y ago
Well, actually, let's explore the metaphor. Here's how I compose music.
First, I use a highly-flexible prototyping instrument to quickly iterate on melodic ideas. Normally this is either humming or whistling. "Our first instrument," as an instructor used to call it. I do this until the melody is catchy enough to start sticking in my mind. Then I'll look at alternate parts, like countermelodies, B/C sections, harmonies, or basslines. Each part gets repeated until it gets stuck in my head.
Then, I repeatedly hum/whistle each part like a disturbed rambler until I get home or to some other place where I can scribble the parts down onto paper. I used to be faster just typing out the Lilypond, but I'm out of practice with that whereas all musicians generally never stop having a short shitty hard-to-read staff-based shorthand.
Now, finally, I actually start playing the different parts on actual instruments, figuring out stuff like fingering for guitar or piano, rhythm and phrasing, filling out transition chords, etc. During this process, I play less and less of the song, drilling down to tiny fragments and setting up tiny local contexts for testing ornaments, phrasings, hits, etc.
If I'm gonna try to make the metaphor rigorous, there would be only one language, because the musical process happens entirely in one language. This language is small enough to write on a single piece of paper, fully contains all of its abstractions, has focused notation for specific instruments, and permits debugging any part of a program by cutting any contiguous subprogram out and turning that fragment into its own live environment.
I'd write a program first on a prototyping platform which is so lightweight that nearly any prototype program will run, and I'd use that to write out the entire first draft of my program. Then, I'd incrementally move pieces of my program transparently onto a more rigorously-precise framework which is more restrictive about types but makes it easier to compose modules and let them stay composed.
There is a point in time when composition is more about managing multiple parts at once and you won't actually want to listen to more than about 15s of your song at once, and you'll want your runtime to support your quest towards stability as a basis for tweaking the fine details of your song.
- didibus 9y agoHonestly, that describes exactly my programming workflow when working in Clojure. The whole process happens in Clojure. This language is small enough to write on a single piece of paper Check, Clojure is one of the most concise language out there. I use it for hand writing code or when coding on my phone, because its so short. fully contains all of its abstractions Yup. In fact, you need to change your mindset, you don't write instructions in Clojure, you search for the functions that do what you want and arrange them in the order you need. Think micro-library. has focused notation for specific instruments Lisps like Clojure are the kings of DSLs. This is literally their bread and butter. and permits debugging any part of a program by cutting any contiguous subprogram out and turning that fragment into its own live environment. Yes, that's what the REPL lets you do. Load any subset of the program into a live running environment. I'd write a program first on a prototyping platform which is so lightweight that nearly any prototype program will run, and I'd use that to write out the entire first draft of my program. This is the idea behind dynamic programming which was invented by Lisp. Clojure will run almost any code, correct or not, no question asked. It does not put constrain on you, though it highly suggests the use of safe tools over unsage ones, it lets you cut yourself if you want too. Then, I'd incrementally move pieces of my program transparently onto a more rigorously-precise framework which is more restrictive about types but makes it easier to compose modules and let them stay composed. This is my ideal too. There's not yet the golden graal for this, but Clojure is currently focused on this very last part. Composing modules is actually pretty trivial, because it supports performant immutable datastructures as first class, open polymorphism, defaults to functionally pure constructs and wraps all state in safe managed containers. Now as for correctness checks, it has optional types, but the implementation is a work-in-progress, and is stagnating a bit. Optional generative tests is the current strategy being explored, as well as highly powerful runtime checks. Still in alpha though. Static analysis is also being worked on, though soundness is not a target, also in alpha at this point. If its any consolation though, the only two studies I could find about defect rates relating to programming language choice showed that Clojure does as well as Hakell. Which means it had some of the lowest defect rates of the languages tested, averaging a tad behind Haskell, and doing better then Scala and F#. Obviously besting Java, C++, Python, Ruby, C#, Go, etc. It was an outlier in that sense, as it was the only non staticly typed check language to do so well.
- DonbunEf7 9y agoUm. Here's the first part of Clojure's docs [0]. Compare and contrast: "A pitch is a frequency. 440Hz is an A pitch. There are twelve pitches. Each pitch differs from the previous pitch by the multiplicative constant twelfth-root-of-two. Twelve pitches in a row doubles the frequency, creating an octave." It only takes four or five paragraphs to mathematically describe the basis for chromatic notation. If Clojure fully contained all its abstractions, then it would not have the option of calling into the JVM. Clojure does not have the property that cutting any fragment of a valid program yields a valid program. There are languages in the concatenative style which have this property, but even there, the ability to compose does not guarantee the ability to split. If you think that types produce reliability, then you do not understand types. You seem dedicated to Clojure, which is great, but music is thousands of years older and has figured out a lot of stuff. I think that you also missed my bigger point, which is that programming is roughly at the same point in its art that cave painting was at hundreds of thousands of years ago. If you think that Lisps are beautiful and Clojure is the pinnacle, then I think that you don't know beauty. But it's not your fault; you've never seen anything beautiful. None of us have. And none of us ever will, at the current rate of progress. Here, have a video to provoke some thoughts: [1] [0] https://clojure.org/reference/reader https://clojure.org/reference/reader [1] https://vimeo.com/74354480 https://vimeo.com/74354480
- didibus 9y agoI think that you also missed my bigger point Indeed I did. I'm not claiming Lisps and Clojure to be the be all end all of programming languages, I hope not. That said, I think you're being a little dramatic. Beauty really doesn't exist, its an illusion fabricated by a mix of our culture and our genetic predispositions. It's mostly characterized by the emotions it evokes in you. Clojure and Lisps, currently of all languages I've tried, evoke the strongest set of positive emotions in me, the strongest one being joy. And I think some of it is not without merit. Still, beauty is not what I'm talking about. I like quantifiable measures. You have productivity, explorativity, understandability, performance and correctness. Clojure finds a good balance between these, it tries to maximise the average of them all. Which is why I find it's great for your average programming project. I don't yet know of a language which does a better job at this maximization, but I'm always on the outlook for one. It only takes four or five paragraphs to mathematically describe the basis for chromatic notation. Its just as short to describe the basis of Lisps, that is the lambda calculus. Also, chromatic notation is less powerful then lambda calculus notation, much less, yet lambda calculus notation isn't much longer to describe. I'm also not sure shortness is necessarily better or more beautiful, again, that's an esthetic preference like minimalism in art. With nine constructs you can have a full Lisp impmementation that can do all turing equivalent computations and also I/O. But in practice, having even more turns out to be useful and makes things easier. I think it's the same for Forth too. If Clojure fully contained all its abstractions, then it would not have the option of calling into the JVM. I'm not sure this has any practical merits, but there's nothing in Clojure preventing this. It could, in fact self-hosted ClojureScript does. It's not very useful, which is why Clojure doesn't bother, and calling into the JVM is actually quite useful, to leverage lots of existing code. Clojure does not have the property that cutting any fragment of a valid program yields a valid program. You can cut anywhere an sexpr starts or ends and get a valid program. Maybe that's not granular enough for you, but what is? In forth you can cut at any whitespace and get a valid program, but I could claim it's not granular enough, I want to cut at any character. To me, the property is that the notation is recursive, it builds on itself. Sure the unit being an sexpr is not as small a unit as a single musical note, but that unit is independent, composable and nestable. If you think that types produce reliability, then you do not understand types. I don't, I didn't use that word at all. In fact I believe Clojure programs to be very reliable, even though they don't have static types. Here, have a video to provoke some thoughts Thanks, always looking for thought provoking thoughts, I have not watched it yet, but will.