3 ms·
The problem with named parameters is that you can theoretically still pass the wrong value, and in practice not everyone is using named parameters on every func
by amzans 8y ago
The problem with named parameters is that you can theoretically still pass the wrong value, and in practice not everyone is using named parameters on every function call.
By relying on the type system you can catch that issue at compile time and not have to worry about it.
We use this technique at work when there’s lots of fields in our data types as it’s nice to be able to keep track of the types during all the transformations and to avoid subtle bugs. Specially as the codebase is constantly changing.
- jph 8y agoI agree with you. My opinion is that type checking helps, and so do assertions, borrow checkers, pen testers, etc. The tradeoff IMHO is that these take some time and thought to do well. Would you be willing to send me an example of your best-case for using a type system? For example some code where you feel the type system really helps you and your team? I keep a log of these real world areas, for training my teams. Anonymized code is fine. My email is joel@joelparkerhenderson.com. Thank you!
- tayo42 8y agoDo you do this for every string, int, float etc..? Any downsides you run into? I always thought it would be interesting to try it using a type even if it wraps something but never had tried going all in on the idea. In scala do you not use alias ever? I guess they don't help much? Alias of strings can be used interchangeably I think?
- amzans 8y agoWe don’t do it for every type, instead on a case by case basis. IDs are usually the obvious ones since we might have a Product class with fields like ProductId, BrandId, List[TagId], SellerId, etc... and it’d be nice to not mess that up. Another example is if you have some pricing info you could group it in a case class like: case class PriceInfo(price: BigDecimal, discount: BigDecimal, currency: Currency) And then put that into your product class: case class Product(id: ProductId, price: PriceInfo, ...) The nice thing about this is that all these related fields can be dealt with in context, i.e. if a function accepts a PriceInfo then it now has access to the price itself and the currency. Not to mention that a price without currency in an e-commerce app can be quite ambiguous.
- clausok 8y agoI've found there are situations where primitives are fine and wrapping doesn't add much value, e.g., (using F#) let calcCorrelation (symbol1:string) (symbol2:string) = ... Here, calling the function with misordered arguments won't change the result, so the wrapping\unwrapping friction doesn't have much ROI. but here, where 'beta' is the slope of a least-squares fitted line: let calcBeta (stockSym:string) (indexSym:string) = ... that's a mistake waiting to happen, because if you accidentally flip the arguments when you call the function you get a different number. Better to have: type StockSymbol = StockSymbol of string type IndexSymbol = IndexSymbol of string let calcBeta (IndexSymbol index) (StockSymbol stock) = ... then call the function like this: let beta = calcBeta (IndexSymbol "SPX") (StockSymbol "AMZN") Of course you could still make a mistake: let beta = calcBeta (IndexSymbol "AMZN") (StockSymbol "SPX") but that would stick out like a sore thumb.