3 ms·
I don't disagree. It's just more tedious in static languages. Let's say we're doing a user registration. In most dynamic languages the JSON body will get parse
by latch 9y ago
I don't disagree. It's just more tedious in static languages.
Let's say we're doing a user registration. In most dynamic languages the JSON body will get parsed into a map. Excuse the fat controller and pseudo-language, but it'll end up looking something like:
func create(conn, params) do
if not Validator.is_email?(params["email"]) do
return error(conn, "email is not valid")
end
if not Validator.min_length?(params["password"], 10) do
return error(conn, "password must be 10 or more characters")
end
create_user(params)
true
end
In Go, if you want this nice and typed, you'd add a RegistrationInput struct with the field mapping tags:
type RegistrationInput struct {
Email string `json:"email"`
Password string `json:"password"`
}
Map the user's input to the structure, handling the error:
var input RegistrationInput
if err := json.Unmarshal(req.body, &input); err != nil {
...
}
And then write the exact same validation checks.
What safety does Go's version buy you?
- Pxtl 9y agoIn C# MVC, the JSON gets deserializrd into a tree of statically-typed objects you declared beforehand. Any malformations of the JSON are an error.
- mercurial 9y agoWell, the dynamic version won't complain until runtime the day you mix up the order of your parameters and invoke "create(params, conn)", for instance. Besides, you can make it statically impossible to generate an invalid user object with a static language. In a dynamic language, an invalid user object is just an assignation away. Now, if we are talking about error handling, the way it is done in Go is not particularly representative. Most static languages would go with exceptions (or maybe checked exceptions in Java). Haskell/Rust/Scala would probably go with a Result type, and then it's more a question of the language giving you the tools to propagate the errors and wrap them (Rust with error-chain is excellent at this kind of thing).
- hota_mazi 9y agoYour example actually illustrates why dynamically typed languages should be avoided in general, especially when manipulating data coming from outside. Your code starts by making a few checks on the shape of the data you're receiving and failing earlier if some conditions are not met. Great. Once these checks are cleared, you now know that you can materialize an object with valid values in it, something the rest of your business code will be able to manipulate. So you instantiate that new object, return it and... all the knowledge about the shape of that data is lost. The only thing your callers will see is an Object and zero information about what operations or fields are available on this object. They'll have to guess. With a statically typed language, you don't have such a problem. You have asserted all these properties about the data that came in, you know exactly the class of the object you are creating, why forget all this precious information that will keep your code robust? In my experience, dynamically typed languages are at an evolutionary dead end and offer close to zero advantages over modern statically typed languages (e.g. Kotlin, Swift).
- harpiaharpyja 9y ago> Once these checks are cleared, you now know that you can materialize an object with valid values in it, something the rest of your business code will be able to manipulate. So you instantiate that new object, return it and... all the knowledge about the shape of that data is lost. I'm not really seeing how that necessarily falls out of the example given by GP. Could you go into that in more detail? Is there something preventing create_user(params) from emitting an object with a well defined interface?
- hota_mazi 9y agoThat function does create an object with a well defined interface, but because you are using a dynamically typed language, that object has the type "Object" (or Any, or whatever the mono type of that language is). If you call that function, you have no idea what the type of that object is. Only the function knows it and that information is lost as soon as the function returns.
- harpiaharpyja 9y agoI think I will have to disagree with that. The type of object is not enforced by the compiler, but that's a long way off from "you have no idea what the type of that object is."
- hota_mazi 9y agoThe fact that the compiler cannot enforce it is a big deal since it prevents automatic refactorings, often impairs performance and overall, hinders readability. And yes, you really have no idea what the type of that object is unless you look at the source of the function. Which is obviously suboptimal, especially if you don't have access to that source.
- seanwilson 9y ago> What safety does Go's version buy you? A statically typed version guarantees you haven't made some fundamental mistakes and enables automatic refactoring. Say we wanted to change an ID field from being a string to an int later in development for example. You've now got to hunt around all your files making sure the right type is being used. It feels incredibly primitive when you need to do this manually or have to rely on an exhaustive and annoying to maintain test suite for this.