Fork me on GitHub
#off-topic
<
2023-06-22
>
Ben Sless03:06:54

Possessed of some insanity, I wrote an instaparse grammar for protobuf files (I hate protobuf)

šŸ˜† 3
hiredman03:06:13

How much testing have you done?

hiredman03:06:33

Because the grammar google published is incomplete

hiredman03:06:42

I know someone (not google) claims to have a complete bnf'ish grammar for proto files, but I haven't looked at parsing proto files since before that

Ben Sless03:06:22

Minimal, but it works on the examples google published in the docs Next is to start testing it on real examples And I can't parse comments yet

Ben Sless03:06:55

Cursed format

Ben Sless03:06:32

But this is not official or endorsed in any way AFAIK

ghadi03:06:20

I made one of these a while ago...

ghadi03:06:50

but I eventually went with a different approach than parsing .proto defs

ghadi03:06:13

remembering vaguely, but I think I made a protoc plugin in golang that converted proto -> json using the proto-in-proto descriptors

hiredman03:06:57

Yeah, not official, but the official grammar doesn't match files in protoc's test suite

ghadi03:06:47

, /opt/homebrew/Cellar/protobuf/23.3/include/google/protobuf/descriptor.proto is the meta proto

Ben Sless03:06:48

Such a pain to work with

Ben Sless03:06:00

Is that plugin published?

ghadi04:06:16

enjoy the comments

šŸ™ 4
ghadi04:06:00

the descriptor.proto might not be the right one

ghadi04:06:53

but the strategy is the same... the protoc compiler plugins give you a defined format that you can output to json directly, then consume in clojure without some bespoke parser

ghadi04:06:20

parsing is insufficient, need to resolve type references, etc. not impossible, just annoying

Ben Sless04:06:41

comments? šŸ™ƒ

Ben L06:06:56

are there any people here who are interested in a lisp implementation on rust? have seen only small implementations e.g. MAL

šŸ‘€ 2
phill22:06:18

Prefix by saying I am 100% ignorant on all the subjects involved, especially Rust!. Specifically, the one thing (that I thought I knew) about Lisp is that it requires garbage collection. And the one rumor I had heard about Rust is, no garbage collection. So Lisp-in-Rust would seem to be a fun puzzle for people who like hard problems. I googled and found at least the beginnings of a few Lisp "interpreters" in Rust, but no "compilers". At a page linked by @UEENNMX0T, I learned that Rust is not very dynamic. Which reminded me that I had heard that new Clojure ports could be based on CLJS more easily than CLJ because CLJS is more thoroughly bootstrapped on protocols. Recently, ClojureDart came out, which reportedly involved a lot of work on static analysis to infer types because Dart is not super dynamic. So, would the lower 5/8ths of ClojureDart be an interesting foundation for the impossible task of a compiler for Lisp-in-Rust?

mauricio.szabo23:06:55

I don't think it's that usable, honestly - I actually prefer the work on #C03SRH97FDK because it'll basically target the same things (close-to native performance, can bring Clojure to places that we don't have now) and few of the downsides

Ben L04:06:58

@U0HG4EHMH yes.. ultimately you need a GC but for toy language which can cover some basic cases maybe not. i'm mostly interested in cross compiling a simple LISP from rust into WASM

Ben L04:06:03

so my target is a runtime which can share a parser, not just server, and then the expressions shared are edn like expressions. shared edn/data doesnt need GC

Jorge Viana20:06:02

Hi all, Do not know if this was already discussed, probably yes, but would like to hear your opinion. A long time ago I read in SICP something along the lines ā€œitā€™s better to have 100 functions that work on 1 data structure than to have 10 functions that work on 10 data structures.ā€ Ok, I think I got itā€¦ I thinkā€¦ I think itā€™s in part a critic to OO. In OO everything needs to be an object and then the functions that work on an object donā€™t work on another. When I use Clojure it makes sense to work with raw data and follow that principle of reusing many functions (the adage from SICP), but when I use Java I donā€™t do it this way, I just follow the OO approach because it feels more idiomatic. But then I read that Clojure has protocols/records which seems to go in the direction of the OO style. I was wondering if protocols/records is a thing that people really care about or if they just prefer to work with more raw data structures. Many thanks!

šŸ‘ 4
eggsyntax21:06:14

I think you'll get pretty different opinions from different people, but I don't tend to use protocols or records that often; I work much more with simple data structures. The tradeoff that comes with that IMHO is that those data structures, if they're complex, aren't self-explaining in the same way that classes at their best are. I tend to address that problem with clojure.spec or malli -- I can use those to say "the structure coming into this function should be this sort of domain entity, as a map with these keys".

šŸ’” 4
nikolavojicic21:06:33

It is better to have 100 fns that work on 1 abstraction (protocol / interface) then on 1 concrete data structure.

šŸ’” 4
nikolavojicic21:06:39

All clojure data structures implement various interfaces. That's why you can use same core functions on all of them. E.g. into can be used on any collection.

nikolavojicic21:06:10

There is also this one if you use transducers šŸ™‚

Jorge Viana21:06:22

@U077BEWNQ thanks, I heard about clojure.spec before. I had forgotten in the mean time šŸ™‚ Thanks, learned a lot from your comment

šŸ‘ 2
Jorge Viana21:06:42

@U011LEAEURE, thanks also, still trying to digest your comments, but they look very smart šŸ™‚

Jorge Viana21:06:05

@U011LEAEURE, so if I create my own abstraction and implement the right interfaces, the core functions will work with my data types, right?

nikolavojicic21:06:08

Yes. You can write your impl for core interfaces. And everything else will work without change.

seancorfield21:06:54

(sometimes that can be very tricky -- some core abstractions have a lot of methods -- so you might want to consider other paths before trying to go down that one)

āž• 2
jpmonettas21:06:32

In my case what I tend to look at is, if I'm modeling data in open information systems then I like maps for entities + spec (or malli), since you don't need to coordinate with other parts of the project about adding/removing stuff from entities. On the other side if memory footprint is important, and if I'm going to have millions of some entities with fixed fields on them, then y create types. Records for me are kind of a middle ground.

šŸ’” 2
šŸ‘ 2
seancorfield21:06:52

It's fairly rare that people create their own Clojure-compatible data structures. It is also made a bit more complex because Clojure abstractions are implemented on top of not just (Java) interfaces but on top of a hierarchy of abstract (Java) classes -- rather than prototypes. So it can be hard, sometimes, to take some external "thing" and bend it to conform with Clojure's collection semantics (for example).

šŸ’” 2
šŸ’Æ 2
jpmonettas21:06:31

I really like that aspect of modeling with clojure maps which remove types coordination points. You can have a project with multiple libraries or sub projects that they can talk to each other dynamically. When you create types you need to coordinate more in what types to have, what fields should be added/removed or can be null since you can even break compilation. But also agree that not every system benefits from that dynamism.

šŸ’” 2
Jan21:06:45

I also come from an OO background and the openness of maps vs classes and the idea of having many small functions that address specific pieces of the data made me realize that I used abstractions in OO in places they were not needed. I rarely have a need for protocols as I seldomly reach the level of abstraction that would require them.

āž• 6
šŸ’” 2
seancorfield21:06:48

And using qualified keywords in hash maps makes it even better since you can convey domain semantics better and you can avoid name conflicts more easily, e.g., :person/name, :account/name etc.

ā˜ļø 2
Jorge Viana21:06:57

Learned a lot so far! Glad I posted this question, I had this in my mind for a long time and never talked about it. Itā€™s really nice to be able to get this much feedback.

Jason Bullers22:06:53

I've wondered the same, so thanks for raising the question. While I think I "get it" in a sort of vague way that the way solutions are framed in Java vs Clojure are different, there's still that itch in my brain that says it's sort of splitting hairs and if you squint, it's all the same thing. What I mean by that is that even though Clojure has a bunch of functions that operate on data, you still need to know (to some extent) what kind of data you're operating on. It happens that, in large part, the core functions operate on the sequence abstraction (interface), and all the data types fit or can be massaged into this abstraction. But it's still just an interface with implementations at the end of the day, no? Similarly, there's core functions like contains? which can't just take any argument: they expect an associative data structure, so once again, the function needs to know the kind of thing it's called on (interface). There's other functions too which only operate on certain types of arguments, like assoc or disj I guess the point I'm making, if anything, is that "good" OO is still under the hood there, but the framing is around data flow and using built-in stuff rather than making bespoke interfaces and implementations. You could manage the same (ish) coding against Java collections everywhere, but it would be horribly non-idiomatic (and you'd lose all the compiler benefits) I'm still pretty new to Clojure, so maybe I'm way off base with my thinking, but that's the general feeling I get from what I've seen so far.

šŸ‘ 2
hifumi12323:06:32

> In OO everything needs to be an object and then the functions that work on an object donā€™t work on another. Iā€™d be careful with generalized statements like this. You donā€™t have to make literally everything an object. And you can have functions that work on object A work on object B, too. See: Common Lisp, Smalltalk, Objective-C, etc. In particular, with CLOS, you build up methods with ā€œgeneric functionsā€ then write methods dispatching on classes like so:

(defgeneric f (x))

(defmethod f ((x type-1)) ...)
(defmethod f ((y type-2)) ...)
Likewise, in Smalltalk and Objective-C, rather than ā€œmethodsā€, you have ā€œmessagesā€. You can take an object and ask the runtime whether an object will respond to a message or not. You can also define ā€œprotocolsā€ in Objective-C, which is a fixed set of messages that an object ā€œimplementing the protocolā€ is guaranteed to respond to.

hifumi12323:06:26

IMO most of the complaints I read online about object-oriented programming are really complaints about C++ and Java, and not the concept of OO itself. Itā€™s really easy to mistake OOP and FP as these incompatible, extreme ends, but theyā€™re really not: Common Lisp is multi-paradigm and you get to use what makes sense for your problem. Itā€™s not uncommon at all to e.g. define a red-black tree in a purely functional way in Common Lisp (since that is easier than the procedural implementation), but otherwise write procedural or OO code elsewhere. Clojure tries its best to nudge you towards pure functional programming, which has its pros and cons.

šŸ‘ 6
lambda-prompt 2
2
didibus08:06:29

There's a time and place for each, but generally people will work with the standard immutable data-structures of Clojure most of the time.

didibus08:06:31

Also, records in Clojure can operate like maps, and in fact can become maps if you modify them in certain ways. So all those 100s of map and sequence functions also work as-is on records.

šŸ‘ 2
didibus09:06:45

@U04RG9F8UJZ Interfaces were not part of OO until only more recently. OO also has if, else, case/switch, variables, for loops, etc. but it's not what characterizes OO. Similarly, interfaces are not what characterizes OO. FP languages have been doing polymorphism for just as long. OO is more about the data encapsulation behind methods. I'd say the unique characteristics of OO are programming through objects, encapsulation, and inheritance. An Interface is actually a very non-OO thing, because it has no data or data definition. It's a set of only functions. And many FP languages have something similar.

šŸ’” 2
šŸ‘ 2
Jason Bullers00:06:59

@U0K064KQV fair, but that's part of my "confusion", if you want to call it that. Everything feels very nebulous and spectrum-y. Arguably "interface inheritance" is totally a thing (because it does, at least conceptually, model an is-a relationship), and until Java 8 introduced default methods, one of the limitations of the language was that you either used extends and got the trifecta of inheritance of type, behavior, and state, or you used interfaces and got only type. But what if you wanted behavior without the state baggage? Inheritance often gets talked about in "good OO" as being something to avoid (or at least deep inheritance trees and inheritance of state), so I wonder if it's really a defining characteristic. Encapsulation you can do even in C, so that's arguably not defining either. And communication via objects is sort of true in a tautological sense. But is there really a huge difference between passing a collection that participates in the sequence abstraction to the map function, vs calling map on a collection that is-a sequence? FWIW I see where you're coming from and don't disagree. I think the distinction is pretty subtle though (depending on the constraints you give yourself I suppose) and that's part of why the whole OO vs FP holy war I've seen out in the wilds of the internet makes so little sense.

seancorfield05:06:29

map and others can operate on things that are seqable -- they don't have to be sequences, they just have to be things that can be coerced to sequences. That's outside of the type system.

seancorfield05:06:05

But, yes, you could write full-on OO systems in C. X11 and Motif were "good" examples of that and I did a lot of that programming back in the day (and, OMG, it was painful). I think it's very hard to nail down a singular definition of OOP because Clojure certainly has "a la carte OO" constructs -- in many ways, purer than Java or C++ -- and yet most people would say Clojure is primarily a functional language.

didibus08:06:04

@U04RG9F8UJZ Ya, it's subtle in some ways, but also very different in the concrete. Like if you tried to implement an OO vs FP language, you'll see it's very different what you'd end up doing. The thing is that they are programming styles. So like writing styles, it's all still similar in a lot of ways, there will be things that are the same or almost, but the overall style when all is put together will feel very different. And it can get a bit difficult to follow if you look at a particular language, because most language evolve to support many things, even if they have one style that is dominant. To me, the key characteristics of the OO style are things like: method lookup and dispatch, classes and inheritance, subclasses and specialization, self types, delegation, etc. If a language offers constructs and syntax around those things, and they are what you end up predominantly using when coding in it, it is an OOP language. You could write Clojure with only defrecord, deftype and protocols, and it would feel a lot more OO, though it would still seem to be missing your typical self type, sub-classes, inheritance, etc. This is why Rust doesn't consider itself an OO language per-say, because it uses traits exclusively, and that ends up giving it a different feel. And that's why I like to use the word "programming style", because it encapsulates a bit more that fuzziness, but also recognizes that you know it when you see it, there is a material noticeable difference, but pinpointing it is almost an art. And shoutout to one of my favorite books: https://www.amazon.com/Exercises-Programming-Style-Cristina-Videira/dp/0367350203/ref=tmm_pap_swatch_0?_encoding=UTF8&amp;qid=&amp;sr=

šŸ‘ 2
Jorge Viana10:06:50

@U04V70XH6, a bit off topic, but curious when you said you can write OO in C. I don't know C but can you do dynamic dispatch there? I heard that you can group functions and data together, but for many people an OO language must have dynamic dispatch.

hifumi12311:06:18

I assume yes because of GObject and Objective-C both being object systems implemented entirely in C. (In fact, Objective-C syntax is syntax sugar over calls to functions declared in objc/runtime.h)

seancorfield16:06:26

@U05C4RCR8BT you have to explicitly use pointers to functions for dispatch but, yeah, the end result feels very OO. Ugly, but workable, and Motif was very OO in style as I recall.

šŸ’” 2
Jason Bullers16:06:36

@U04V70XH6 > map and others can operate on things that are seqable -- they don't have to be sequences, they just have to be things that can be coerced to sequences. That's outside of the type system. > Is it? I'm not sure how it's implemented, but I assumed seqable was something like Iterable in that it specifies a type of thing that can be turned into/provide a seq (iterator) representation.

Jason Bullers16:06:58

@U0K064KQV I like the analogy to writing style. I think that's it really: languages give a natural slant to style with the tools and idioms they provide. Further to that, the constraints applied by convention and agreed style push one direction or the other along the spectrum

Jason Bullers16:06:52

Like how with records, streams, lambdas, switch expressions and sealed classes, you can write Java in the "functional style" and be a bit more data oriented, but that doesn't magically give you functions as a first class language element

seancorfield17:06:05

Seqable is a bunch of runtime checks (null, several instanceof, is array). That's what I meant by outside the type system.

šŸ’” 2
seancorfield17:06:16

Many of the "similar" checks are simple instanceof (`seq?`, coll?, vector?`, map?, etc) so I'd consider those directly within the type system, even tho' they are dynamic checks, since they relate to a simple type (interface in Clojure/JVM, probably protocols in ClojureScript), so there's an implied type-dispatch for related operations -- but seq and seqable? are "shape"-based operations, or "duck-typing" to some degree.

šŸ‘ 2