6 ms·
We found it quite nice for properties of objects that appear over time. Think things like Order that might or might not have delivery details. In C# you are mak
by ambulancechaser 8y ago
We found it quite nice for properties of objects that appear over time. Think things like Order that might or might not have delivery details. In C# you are making classes with nullable delivery timestamps, delivery person, etc. And one or two properties isn't that bad but it gets a little onerous when you start to have constraints like "these four properties are either all null or all populated". In F# it is trivial to set up a new constructor for Delivery that includes all of these properties. There is no unspoken agreement about that, you can set it in the model.
- algorithmsRcool 8y ago> There is no unspoken agreement... Yes! That is exactly right, F# give you the tools to express your actual model with little/no ambiguity.
- contravariant 8y agoI see, although it looks like you're really after a state-machine there. Which I admit aren't the easiest to create in C#, and immutability and null-safety definitely make it easier... Although personally I'd rather have the different states encapsulated in separate classes than a single type that encapsulates all possible states and enforces them through the constructor.
- victorNicollet 8y agoThis is what F# does (define multiple classes), but each of these classes only takes one line of code.
- sparkie 8y agoAlso, it defines them as nested classes, and marks the outer class as `sealed`, as they're closed types, and it prevents the type being extended with new cases. C# can't do this because the compiler rejects putting `abstract` and `sealed` on the same type.
- contravariant 8y agoYou can kind of solve this by making the constructor of the outer class private and defining all subtypes as nested types. It's not ideal, but it works.
- mavelikara 8y agoWhy do you struggle with this in C#, especially given that you are familiar with the F# style? I don't know C#, but in Java I'd write this as: public class Order { final Optional<DeliveryDetails> delivery; public Order(Optional<DeliveryDetails> delivery) { this.delivery = delivery; } } public class DeliveryDetails { final long deliveryTimeMs; ... public DeliveryDetails (long deliveryTimeMs,...) { this.deliveryTimeMs = deliveryTimeMs; ... } } My IDE writes most of these lines for me. I believe C# will have similar or more succinct constrcuts.
- ambulancechaser 8y agothat's true, you can recreate it in some sense but you can't get to what F# can guarantee. A big promise of discriminated unions is the ability to make invalid state unrepresentable. Matching on the different type constructors is a fantastic way to only express coherent states.
- uryga 8y ago> Matching on the different type constructors Terminology nitpick: Pattern matching is done on value constructors (or just "constructors", but at the intersection of FP and OOP that could be confusing). "type constructor" means something like `List` (as opposed to `List[Int]`) – a generic type that hasn't been applied to any type argument(s) yet, and will "construct" a type (like `List[int]`) when you apply it.
- tigershark 8y agoThe 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.