5 ms·
The ide maybe will write a small part of it, but you’ll have to keep reading it forever. And obviously it is modelled wrong because it is possible to have a Del
by tigershark 8y ago
The ide maybe will write a small part of it, but you’ll have to keep reading it forever.
And obviously it is modelled wrong because it is possible to have a Delivered order without delivery details, there is nothing that enforces it.
Compare it with how I would write it in f#:
type OrderId = OrderId of string
type DeliveryTime = DeliveryTime of long
type DeliveryDetails = { deliveryTime: DeliveryTime...}
type Order = { id: OrderId ...}
type DeliveredOrder = { id: OrderId, deliveryDetails: DeliveryDetails...}
Probably it is even better to define delivery time using the unit of measures and specifying it as ms.
What is the difference in this way?
That an order cannot ever have DeliveryDetails, while a DeliveredOrder must have DeliveryDetails.
As a bonus you can’t just pass any string as an order id (for example a description) but you need to pass an actual order id. the same is true for the deliveryTime with the adddd advantage that you won’t be able to perform operations on it with a different unit of measure.
In java or c# you would kill yourself if you try to do something similar and moreover you won’t have all the constraints specified here and the immutability automatically enforced.
- Dolores12 8y agovery elegant solution. i agree it will take much more time to implement that in c#.
- mavelikara 8y ago> i agree it will take much more time to implement that in c#. If I am reading the code correctly, here is the Java version (with Lombok [1]): @Data class OrderId { final String id; } @Data class DeliveryTime { final long time; } @Data class DeliveryDetails { final DeliveryTime deliveryTime; } @Data class Order { final OrderId id; } @Data class DeliveredOrder { final OrderId id; final DeliveryDetails deliveryDetails; } I can imagine C# also having similar expressive powers. [1] http://jnb.ociweb.com/jnb/jnbJan2010.html http://jnb.ociweb.com/jnb/jnbJan2010.html. Lombok reduces the drudgery of writing some of the code in Java. Modern JVM languages like Scala and Kotlin have native constructs to express this.
- tigershark 8y agoIf you are not writing java and you are using lombok then yes, this simple case seems covered well. I still can’t see how Lombok will help with the exhaustive pattern matching in case the order is a union type of several orders types as explained in my other comment or with avoiding mixing up ms and seconds when using a unit of measure in f# for deliveryTime. Also I’m curious how you would change just one field of an immutable object with 10s of properties in Lombok and if the resulting java code is as efficient as F# with its immutable data structures that use the copy on write semantics.
- mavelikara 8y agoI agree that exhaustiveness check enforced by compiler is something I will miss in Java. Persistent data structures have been implemented in Java if that's what you mean by efficient mutations to immutable structures. I can't imagine such structures being hard in any language hosted on the JVM or CLR. F#, and ML-family languages, surely have their killer features. I am only contesting the claim that the GP made that modeling a domain is a struggle in C# when compared to F#. Imagine if someone came on this thread and claimed that they struggle to write effectful code in F# which they have been writing in Haskell. Of course, you have counter-evidence of that in all the F# programs you have written so far! I feel the same about the inability-to-domain-modeling claim.
- tpxl 8y agoWhat's the difference between this and having an Order class and a DeliveredOrder extends Order class in C#?
- hnbroseph 8y agothey said "an order cannot ever have DeliveryDetails" so, if in a nominative subtyping situation like you suggest, a DeliveredOrder is-a Order, and so we can see that some subset of Orders CAN have DeliveryDetails. any method receiving an Order could receive a DeliveredOrder.
- contravariant 8y ago> any method receiving an Order could receive a DeliveredOrder. This seems reasonable, if not outright desirable.
- hnbroseph 8y agoperhaps, perhaps not. in the abstract there's no way to valuate it. it depends on what it means to be an Order and what it means to be a DeliveredOrder, what assumptions are made by code that receives an Order, etc.
- tigershark 8y agoNo, most of the time is wrong. If you have a DeliveredOrder you want to make sure that is not delivered again, so the delivery function should accept only an Order, not a DeliveredOrder. If in some different system you need a domain object that is both an Order and a DeliveredOrder than in F# you simply use an union type: type Order = UndeliveredOrder of UndeliveredOrder | DeliveredOrder of DeliveredOrder And in this way you can write a function that accepts both an UndeliveredOrder and a DeliveredOrder.