Fork me on GitHub
#off-topic
<
2022-09-19
>
Ivar Refsdal08:09:51

I figured I would share my Clojure (+ misc. Norwegian characters) layer on my keyboard. The layer is toggle-able with a single keypress. I'm pretty pleased with the setup 😊 https://configure.zsa.io/moonlander/layouts/4DjW4/latest/3 Edit: I also "dig" the moonlander keyboard 🙃

❤️ 3
🆒 2
Stuart08:09:04

What does this keyboard do for Clojure dev ? I've thought about making my own custom Clojure keyboard before with custom keycaps / symbols for common functions I use, with shortcuts setup for those in VSCode. SexpUP must be for expanding Sexpression ?

Stuart08:09:33

Also, is your keyboard actually split like that ? Pjysically into 4 bits ?

mdiin09:09:49

Interesting! I have a standard Ergodox EZ which is my daily driver, with a highly customised layout. I never thought I needed to make a language specific layer though, but I will look at yours and see what I am missing. 😀

1
sheluchin09:09:00

Cool. I keep thinking about making a Clojure layer for my Ergodox but I can't seem to decide exactly what to do because Vim already covers many things with just one or two key strokes.

🧡 1
mdiin09:09:16

@U013YN3T4DA The Moonlander is split in two halves, where each half has a thumb cluster, which is why the configuration UI makes it look like four parts.

1
Ivar Refsdal10:09:35

> What does this keyboard do for Clojure dev ? It just keeps/makes things comfortable for me, I think. > SexpUP must be for expanding Sexpression No, it's for navigating up (and left). Match br is for match bracket: (, [ or {. I'm a Cursive user by the way.

Ivar Refsdal10:09:10

Actual keyboard

Ivar Refsdal10:09:01

I imagine vim can/might solve many of these problems

Stuart10:09:11

Did it take a lot of geting used to a split keyboard ? I guess the muscle memory is the same, as the same fingers will still be hitting the same keys, just your hands are further apart. SO maybe its still quite intuitive

Ivar Refsdal10:09:41

No, didn't take much time. I've previously used some microsoft split keyboard though, and even a Kinesis advantage many years back (that did take time). A co-worker (ergodox user) said it took him quite some time though. I also think it's beneficial for posture, i.e. I won't be too hunched over a narrow keyboard. I should also note that I picked the kalinh red key caps or so, it felt just the right amount of pressure for me. Another co-worker had a key caps test board, so I could try out before I chose one. To clarify regarding the Clojure layer: I just tap one key and that makes the keyboard enter the Clojure layer. Tap another key that does the action (slurp, barf, next error, etc.) and then it exits the Clojure layer.

Ivar Refsdal10:09:03

"Microsoft Natural Ergonomic Keyboard 4000" was my previous keyboard to be precise...

sheluchin10:09:22

I found my hands were kind of sore for a while when I first switched to the Ergodox (from just using my built-in laptop keyboard for a long time).

mdiin10:09:53

I had that same issue, and just came to the conclusion that my hands are too small to effectively use the thumb cluster a lot. After I changed my layout to only use the two large keys on each thumb cluster for space and enter, I stopped having issues.

mdiin11:09:27

This was 4-5 years ago now.

sheluchin11:09:40

I only use the two large keys as well :thumbsup: The thumb clusters are generally pretty awkward. I never even tried to use the other keys there. I've been thinking about making them perform some action in "clojure layer", but they are still an awkward reach and I prefer to put everything as close as possible to the home row. I believe the Moonlander design is largely meant to address the awkwardness of the Ergodox EZ thumb clusters.

mdiin11:09:17

I have mine here: https://configure.zsa.io/ergodox-ez/layouts/xbZxp/latest/0 It’s based on the MacOS US international layout, and puts Danish letters on a separate layer. Mine’s a work in progress as well 🙂

1
Ivar Refsdal18:09:17

I agree the thumb cluster on the EZ is mostly too hard to reach. I use all the three "regular" thumb keys on the moonlander, and sometimes the "pointy" thumb keys. The right "pointy" thumb key is bound to "send to REPL", so a slight pause (should be?) is warranted after hitting that, and thus I don't particularly mind getting somewhat off home row.

Stuart14:09:49

Fuck OOP. I've got a code base at work, an API. I just need to change a /GET method to take 3 extra parameters.... but because of layers and layers and layers of inheritance, and all these mixed over loads of classes and interfaces. I'm having to change 15+ classes.

💯 2
2
p-himik14:09:04

To be fair, you can achieve the very same result in FP. It's just slightly harder because it's much easier not to do it by using and re-using maps instead of unrolling everything or creating new maps every time you do something to some of its items.

p-himik14:09:02

And it's possible to avoid that in OOP just as well. It's just that it's slightly harder to avoid it. :)

Stuart14:09:38

We have a thing that runs on check-in, that will reject code if their is any duplication of blocks. So if anywhere in the code more than 3 lines of code are the same, it needs to eb be pulled out to a common class. Its ridiculous, there are so many layers of inheritance

😬 3
😂 2
p-himik14:09:02

Hah, yeah, that's a bit too much.

Martynas Maciulevičius15:09:35

Common inherited class? Why not util class? This would avoid inheritance

Noah Bogart15:09:06

good god, that's a Not Good reason to reject code

☝️ 3
Martynas Maciulevičius15:09:17

I think you should make one more rule: If there are more than 3 levels of hierarchy you reject the code. This way you'll have way better time If somebody has started to do a "garden of eden" then why not go all-in 😄

Stuart15:09:31

I had to game it the other day. I had a duplicate logging message, that spanned a few lines with line breaks for readability, had to just slightly change the logging message, and a big query with joins, it spotted a duplication between 2 files for a partial Where , so I had to swap a few predicates around in order for it not to reject it 😄

😂 2
Martynas Maciulevičius15:09:02

You could also obfuscate your sources 😄 I remember you do C#, so probably this is what you would use: https://csharp.hotexamples.com/examples/-/Uglifier/Uglify/php-uglifier-uglify-method-examples.html Edit: Unfortunately my link is for JS, not for C#. But you could find something that does it. Or you could write it. It's important to pass the builds after all... Edit2: But... you could convert your classes into JS and then commit them. Then you could uglify them. And when building it could transpile them back into C#.

😂 2
p-himik15:09:52

And send gzipped blobs for code review. :D Unzip and compile at run time.

😂 1
didibus19:09:31

> To be fair, you can achieve the very same result in FP. It's just slightly harder because it's much easier not to do it by using and re-using maps instead of unrolling everything or creating new maps every time you do something to some of its items. > In FP potentially, but in DOP (data-oriented programming) not so much.

didibus19:09:45

> And it's possible to avoid that in OOP just as well. It's just that it's slightly harder to avoid it. :) > I'm actually curious how? In a prototype based OO I can maybe see how, but in a class based OO I really don't, and I've been experimenting to figure it out.

p-himik20:09:13

I assumed that a particular problem Stuart is dealing with is due to the need to pass through those extra parameters throughout the whole chain of all calls that care about them. If so, all the parameters could be put into a special type that you'd pass to relevant classes. So later on, instead of changing 15 classes, you'd have to change just those that actually do care about those parameters and that one that contains all the parameters.

didibus20:09:05

I'm not sure this would count as OOP anymore. If you simply injected an additional class that is the union of all possible data all your other classes might ever need to operate over, and made each value optional on that shared class... Basically you're saying, let's not have methods operate over data encapsulated in the class, but basically let's just have methods operate over a shared object of all the data all the classes might need.

didibus20:09:44

This is actually the challenge I've faced with OOP, it is very hard not to couple all your classes together, or end up with a shared class that is used everywhere. The only solution to this, is the invention of a multitude of complicated OOP patterns, which end up creating this layer of layers that OP describes and hates so much. So in my opinion, it's like a fundamental design flaw with OOP itself, that needs really round about solutions to avoid, with ton of intermediate classes to represent all intermediate states and Data Transfer Objects, mappers, inheritance chains, mixed in composition, etc.

p-himik20:09:11

> let's just have methods operate over a shared object of all the data all the classes might need. Almost - instead of "all the data all the classes might need" it's "all the parameters that are pertinent to HTTP requests". I'm not suggesting adding there, say, a DB connection pool instance. Of course, if you never needed a DB connection pool instance anywhere in an chain of 15 classes and now you suddenly do, using such a pattern won't help you. But in the FP/DOP world, you also would have to change 15 functions, assuming all they did before was to receive data from an HTTP request. At least, I myself would prefer for it to be done that way - to avoid mixing disparate stuff too much. And if such functions already have an argument like e.g. ctx that points to all the instances of injected dependencies - well, that's exactly how it would be possible to also solve it in Java. Although then you start getting things like Spring and whatnot.

didibus20:09:50

In OOP, you can't use the http request params as-is, they too have to be converted to nested classes of classes though, that's already a lot of classes/layers just for this one task. And then you have the question of where to do the conversion/validation, and what if you need to enhance/transform any of it through that chain? Etc. I think in practice it gets tricker because of all that. You can't just transform a value on a map so from this point on the value on that key is the transformed one. So now you've got yourself two version of that class already, a class of classes with the http-requests data on it, and a class of classes with http-requests that have been transformed in some way. Though, I guess maybe this is a bit tied with nominal typing, the class describes the structure, so maybe if you typed everything as Object you could do this a little bit, but it would also get trickier if you needed to add intermediate data through the chain, like say you need something from the DB and something from the HTTP request. Now you need a new class with both those keys, or again, you need to start making a union of all things class, which breaks OO design principles. > But in the FP/DOP world, you also would have to change 15 functions The issue is not changing the functions (or methods), it is with changing the data itself. The class make your data very static, and every single variation on the data-representation/structure, including just having a bit more or less data, requires a new class and mappings from one another. Furthermore, the functions (or methods), are actually not coupled to the data, but to the specific class that models the data, so even a function that needs a subset of the data, even if the class/object has exactly that data in the right structure/representation, well that function cannot work without coupling itself to the exact class that has a lot more stuff on it as well. And this is one place where you'll now start to see people using inheritance to address this challenge.

didibus21:09:18

Just a small example, say you have some logic going through a chain of A -> B -> C -> D. Now say that C->D rely on some particular set of data represented in some specific way. And say at first, A->B also relies on the same. So you've got a shared class representing exactly that set of data in that exact shape/structure. Now, for some reason, say B needs more/less data, and it also needs it represented in a different structure. Since A->B->C->D all share the same class, changing that class to accommodate B breaks all layers. You can't just start with a different data-structure (as a class), from A->B and then when B calls C it just transforms its data-structure to what C->D expect. You now have to break that class into two, breaking either A->B or B->C, and then map from one to the other. And since it's likely that the class was actually a class of classes, its likely you actually broke many classes breaking a lot of code, for what is a simple change. Ya, you can refactor it all, but it creates an accidental need for all this refactoring in my opinion, because OOP just pushes you to couple all layers with shared classes like that, and avoiding it requires a ton of scaffolding, a lot more classes, mappers, etc. again which is where you start yo have layers between layers just for this stuff. And to make matters worse, what appears to be simple data transformation, but when what you have is an Object instance of a class, it turns out you can iterate over its elements, see the count of items it has, do a depth first traversal in a generic way, etc., because its not a generic data-structure with all these operations already supported by it natively. So that transform often requires a lot more work, or to pull reflection based libraries that do object mapping.

Lennart Buit21:09:23

Some people really like DRY. I was delighted when I found ‘avoid hasty abstractions’ (AHA). I don't know, working in the wrong abstraction just encourages extending it opportunistically and almost never leads you to the right one.

paulocuneo01:09:03

terrible things have been done I the name of "re-use"

👍 2