Fork me on GitHub
#off-topic
<
2023-11-08
>
Pavel Filipenco16:11:23

I think I'm starting to feel the pain of type systems and the thing to which Rich Hickey referred to in «Spec-ulation» and (partly) in «Maybe Not». Type systems require either a new type for any form that might have a date, so with a Maybe<Date> field, or they require for me to change an existing type and put some default value in all the places where it's used but doesn't really matter. Or maybe it's just me, maybe I just built a bad complex program.

Pavel Filipenco16:11:45

Maybe I'll be able to migrate to Clojure while it's not too late for such migrations, but that won't be helpful to people in my organization, since, if Rust is hard for them, I suspect Clojure will be much harder, so they probably won't be too active in maintaining this project. Oh well.

Pavel Filipenco16:11:17

There is, also, another factor. With rust, in the «rocket» framework, there are «request guards». They ensure your request «gets some data», but can also be used for authentication. They are structs that implement from_request. With Clojure, to achieve the same kind of behavior, I would need some kind of admin-only-middlewar, but, since reitit has handlers in one place and the router in another place, I'm actually afraid of how it's decoupled. What if someone forgets to add the admin-only-middleware (or any other middleware for that matter), and my function isn't guaranteed to get what it expects. Also, spec is confusing. However, I just though how rust implements it, it generates code. I guess I'll write a defhandler macro that will add keys into a map, and ensure some crucial ones are in the map. Yeah, that's it. sketching so I don't forget

(defhandler name (request)
  {:post {:key1 ::spec-thing
          :admin-only field-extracting-fn}}
  ,,,)
or maybe there's ideas better than that? Idk

Noah Bogart16:11:26

In reitit, you can apply middleware to route chunks and every sub route will inherit it, as i remember it

👍 1
Noah Bogart16:11:57

In your rust code, that’s the perfect place for “…Default::default()”. Not quite as smooth, but makes such handling nicer.

👍 1
Pavel Filipenco16:11:54

Yeah, but maps feel nicer the more variations of kv pairs come in a request. At the same time, I love the strictness, the guarantees of Rust.

👍 1
Pavel Filipenco16:11:06

it’s a me problem

Pavel Filipenco16:11:29

yes, I. know about default

👍 1
Noah Bogart16:11:56

Haha yeah i have the exact same issue with Rust. I love the strictness until I want a bag of options and then I’m tearing my hair out

💯 1
Pavel Filipenco16:11:18

creating a struct of optionals

Pavel Filipenco16:11:17

Maybe I’ll create a must-have middleware

Martynas Maciulevičius16:11:19

> What if someone forgets to add the admin-only-middleware What if I forget to wash my hands when I come back from outside? This can also be forgotten in Rust.

Pavel Filipenco16:11:36

@U028ART884X, yes but in Rocket the required params are coupled with the handler function. Very tightly.

Martynas Maciulevičius16:11:26

> but in Rocket the required params are coupled with the handler function. Very tightly What if somebody uses wrong function? And maybe requires access that seems ok for the time being but doesn't actually work later down the line? Open this video at 3:20: https://youtube.com/watch?v=aSEQfqNYNAc "But we're programmers, we need protection!" - Rich Hickey

Pavel Filipenco16:11:17

They can’t use the wrong function, since the function is also coupled with routing logic :)

Martynas Maciulevičius16:11:37

You can't prevent people from making mistakes. It doesn't mean that you should use more blunt knife (Clojure has no protection but then what if somebody creates a Rust route function that requests route parameters and falsely claims it works properly?). You still have no protection from human stupidity.

Pavel Filipenco16:11:46

You write a handler, with one rocket annotation, and everything’s ensured via codegen. Clojure doesn’t like coupled things though, it likes composable stuff. That’s the source of my worries. As I said, it’s a me issue, my own paranoia

Martynas Maciulevičius16:11:11

> You write a handler, with one rocket annotation, and everything’s ensured via codegen You write a handler and you need to use this handler to build another handler. Now what? Then you write another annotation that does basically the same thing. Now you have two things that are almost the same but slightly different. Somebody uses one or the other down the line but then you want to do something else with your second annotation. Then you decide to refactor it because type system has you covered but you didn't write the integration test because: 1. this annotation is hard to bootstrap -- you need to spin up a framework for it 2. type system has you covered anyway -- why even write even a single test because if it compiles it works, right? 3. I have only one annotation, why would I want a test -- I can test by hand So you don't write that test and then you have two annotations... five annotations... then you get a bug where you don't remember what the annotation has to do. And then you wish you had the integration test. But now it's too hairy. And Clojure? "Things are not coupled and I should write the integration test right away because stuff might change beyond recognition"

Pavel Filipenco16:11:02

You should check out Rocket before speaking. I don’t need integration tests for functions that don’t do any business logic, and are just CMS glue code

Martynas Maciulevičius16:11:13

If function doesn't do business logic it should be removed as the code doesn't get run. If it's part of the framework then it participates in auth or other logic which is also business logic.

Pavel Filipenco16:11:54

Am I gonna test how a tested library fetches values out of the database? Or am I gonna test how the tested template library renders the well defined contents?

Martynas Maciulevičius16:11:26

> Am I gonna test how a tested library fetches values out of the database? Or am I gonna test how the tested template library renders the well defined contents? I'm not sure why you have this thread. You already answered the question of what you want to do. I'm not sure if you consider a choice at this point.

Pavel Filipenco16:11:39

My whole app is just “check user auth -> upload their file -> return response on success”

Pavel Filipenco16:11:12

This is an off topic rant, I’ll figure it out

Martynas Maciulevičius16:11:15

I was trying to add counterpoints to your arguments of which slippery slope you could take. Clojure will have some rough points and you know it in advance, but types will also have their hiccups which you don't think about in advance -- the main one being false sense of security that you told about when you said "no, it's code but I don't need to test it as it's coupled". And in the end of the day you choose what is comfort to you.

Pavel Filipenco17:11:31

I know the hiccups of types, that’s why I started the thread. I’m considering clojure

Pavel Filipenco17:11:00

I just want my authentication guards to be in the same area of code as the function, not in the routing file somewhere. I’ll just make a middleware and use it together with the functions

Martynas Maciulevičius17:11:02

Or you could create an intergration test which would do a for loop to execute each route in your config and check for 403 :thinking_face:

Pavel Filipenco17:11:01

And then I'll have 3 places to update whenever I add an endpoint that requires authentication. I can do a single test for my authentication wrapper, but then that'd need an initialized database with valid credentials that I'll need to test against

hifumi12300:11:43

I believe this is the infamous "proliferation of types" Rich described, where you end up creating various subtypes where a few optional fields are now required, so functions can take values of this subtype and be guaranteed an optional value is present.

👍 1