5 ms·
Value-Oriented Programming (2018)
- stopachka 7y agoLove the simple demonstration of a pretty profound concept
- ken 7y agoHe doesn't use the term, but this looks exactly like an embedded domain-specific language. It's a nice improvement on the original sample program.
- dang 7y agoDiscussed at the time: https://news.ycombinator.com/item?id=17881197 https://news.ycombinator.com/item?id=17881197
- kragen 7y agoI think this is usually called "functional programming". See for example https://prog21.dadgum.com/26.html https://prog21.dadgum.com/26.html.
- jnbiche 7y agoWhile most statically-typed functional programming offer some approach to composition similar to Swift prototols, the concept goes above and beyond the idea described in the article you posted, which relates more to the idea of programming with immutable data. So similar compositional techniques to Swift protocols would be typeclasses in Haskell, traits in Scala, module interfaces in ML languages (which have to be used at the module-level via functors instead of at the value-level like the other examples), etc. Edit: I should have read to the end. I stopped after reading a few paragraphs of what looked like a basic interface vs inheritance discussion. Turns out, at the mid-point, he introduces an "alternative" to that approach, which is indeed more or less functional programming. As much as I like functional programming, (his "Value-Oriented-Programming"), interfaces are often quite useful when programming in the large.
- kragen 7y agoThe article doesn't seem to be promoting using the Swift protocol approach to composition — at least, that's not what it uses the "Value-Oriented Programming" name to refer to. Instead, it's saying that instead of using the Swift protocol approach to composition, you should return a complicated value of some concrete data type from your function.
- jnbiche 7y agoOh, my apologies. I read a few paragraphs and it looked like a basic tutorial about Swift protocols and inheritance vs composite, so I stopped. I'll read the whole thing. Edit: Yeah, you're right. I read to the end and that's pretty much functional programming as you suggested. I'm not convinced it's always better than using interfaces. Like most things, I think it depends. For example, particularly when programming "in-the-large", I think being able to package up different contexts (like a TestRenderer) into different protocol implementations is often an advantage of that approach, not a weakness, as he seems to imply.
- kragen 7y agoYeah, I'm not sure he's right that one technique is clearly better than the other. They're only separated by a transformation to explicit CPS and a doesNotUnderstand: override, but that isn't in itself an argument that the distinction is unimportant.
- contravariant 7y agoWell yeah, making a function return operations instead of having side effects is basically the whole rationale behind the IO () Monad.
- kootling 7y agoI don't get this. Aren't protocols basically interfaces? So the argument is "Start writing an interface, not a class?" Isn't this common knowledge? This is stuff you learn in programming 101. (I'm not trying to come off as snarky; I'm genuinely curious.)
- deleted 7y ago[deleted]
- mikekchar 7y agoThere are 2 general approaches for design when doing something that is vaguely object oriented. One is to decompose you data into structures, figure out the entity-relationships (often with a diagram) and then finally decide which operations should live on which object. When I first started out in the 80's this was pretty much "the way". You would frequently write descriptions of the domain, circle all of your nouns and underline your verbs. Then you would make data structures with the nouns and make methods with the verbs. A lot of this stuff came out of the Simula branch of OOP. It's the kind of stuff that C++, Java, C#, etc was modelled around. Objects are an encapsulation of data along with their operations. The other way to go is more where the Smalltalk guys went. In this branch of OOP, what's important are the messages. Data is abstracted out and your goal is to refer to it as little as possible. Instead messages are passed around between objects and the objects are responsible for dealing with the messy business of handling the actual data. When you are designing you look for the message flow and then build objects that handle the messages. When one says "Start writing an interface, not a class", I think what is meant is that you should concentrate on the messages that the objects need to react to, not the data within the classes. So you are designing a protocol, not an E-R diagram. There are people who are adamant that only one of these two approaches will result in "good code". Often there is a considerable amount of derision for someone who believes in the opposite approach. The truth probably lies somewhere in between IMHO. There are advantages and disadvantages. When doing a data centric approach, it's very easy to understand the state that you are capturing. But it's also very easy to let the details of the data dominate and to create unnecessarily complex code dealing with that data. Often you end up with god objects and poor decomposition of behaviour. On the other hand, if you go for the protocol approach, often you get designs where the state is almost impossible to figure out and where you have "big bags of functions" because it is convenient. It is difficult to maintain the data abstractions and often the data details leak out, pretty much destroying your design. Design is super difficult and requires a lot of experience of doing it. Different schools of thought and descriptions of approaches are helpful to a certain degree, but the best is to write a lot of code and get feedback from people with experience. My biggest piece of advice for people who are near the beginning of their career (i.e. first 10 years or so) is to try to work on an project (likely your own side project) for at least 5 to 10 years so that you can see it implode under its own weight. The biggest mistake I see developers make is assuming that things that appear to work for a year or two will continue to work as the code base ages. Especially I find people who have formulaic approaches to design often break the design badly in the long term (there are some 3 letter abbreviations that I could splash out if I wanted to pick a fight ;-) ) Unfortunately, these people rarely work on a project for more than a year or two, so you get what I call "drive by designings" -- just shoot it to pieces and drive on.
- thelazydogsback 7y agoYou've also now gone from an immediate-mode renderer to a retained-mode renderer (allowing future transformations on rendered segments, etc.) "for free". But you're also adding potentially millions of new GC objects on the heap that weren't there before, so not so free.
- ryanschneider 7y agoQuite possibly all those `Drawable.paths` sets are going to be stack allocated as long as your renderer isn’t retaining any references to them.
- ken 7y agoHow do you figure? He's not using any reference types, and he's using a language that doesn't even have a GC. It's likely the Set<Path> will use heap allocation internally (there isn't really a way around that), but it's only holding a small value type. Besides, this wasn't a streaming interface before this change, either. In the first version, all elements were held in an array already.
- thelazydogsback 6y agoAFAIK Swift supports GC, though only simple ref-counting at the moment. Yes, it's the Set<Path>s that I'm talking about.
- andrepd 7y agoSo functional programming + type classes?
- rnikander 7y agoI was trying to use more values and protocols but then they made SwiftUI where your model has to be class (ObservableObject).
- zvrba 7y agoUm, he "invented" a command design pattern https://en.wikipedia.org/wiki/Command_pattern https://en.wikipedia.org/wiki/Command_pattern
- einpoklum 7y agoThe article seems to be overly preoccupied with nouns.