6 ms·
Why do you want to run programs that are wrong?
by iaabtpbtpnn 4y ago
Why do you want to run programs that are wrong?
- aserafini 4y agoBecause the property of being wrong is not binary. It might be partly wrong but correct enough for your current situation.
- majewsky 4y agoHow do you know when it's crossed into being just a little too wrong?
- ReflectedImage 4y agoYou can have a catch all exception handler that emails any exceptions that occur to you. It's not pretty but it can be done. :P
- aserafini 4y agoI think a catch-all top level exception is totally fine and not ugly at all! That’s why my Python APIs never fatally crash, they just log an exception and move onto the next request.
- 3pt14159 4y agoYou presume that the program is able to determine that it is wrong. One of the most annoying things moving from Ruby / Python into a language with static types that are checked at compile time is dealing with JSON from an external API. I know, I know. The API could send me back a lone unicode snowman in the body of the response even if the Content-Type is set to JSON. I know that is theoretically possible. But practically, the endpoint is going to send me back a moustache bracket blob. The "id" element is going to be an integer. The "first name" element is going to be a string. Sometimes a disable-able warning is all we want.
- zozbot234 4y agoYou should check what the external API returns anyway. If only because the format of any external API might break in subtler way than just returning a single Unicode snowman, and you'll want to ensure that all such failures are correctly detected as soon as practically feasible.
- nkingsy 4y agoTypeScript can type based on object literals, so while you can't type JSON on the fly, you can type a JSON file automatically. With code gen you can usually just print out your JSON directly from the source and import that as a type into your source code. This is why apis are moving towards specifications that trigger multiple generators. JSON typing is possible without generation, but more cumbersome. interface TypedJSON { string?: string; number?: number; array?: TypedJSON[]; boolean?: boolean; object?: {[key: string]: TypedJSON} } {foo: {string: "my string"}, bar: {number: 4}} Probably makes sense to make the "string" key "s" for minimal payload size impact, or this could also be done by converting standard JSON to this typed representation at runtime.
- Mavvie 4y agoI haven't had problems with that in strongly-typed languages I've used (most notably, C#). If the response payload isn't JSON, or if it has a missing required field or something is the wrong type, then JsonSerializer.Deserialize(...) will throw an exception. I don't have to add more code just to make the type check pass or anything. (And if I did, _usually_ it's around nullability and the "!" operator would do the trick, or "?? throw new ImpossibleException(...)") And within the bounds of pragmatism, it's nice that it actually checks this stuff and throws exceptions. In Ruby if the ID field is the wrong type, the exception won't happen at time of JSON.parse(...) but instead much later when you go to use the parsed result. Probably leading to more confusion, and definitely making it harder to find that it was specifically an issue with the JSON and not with your code.
- masklinn 4y agoMaybe look at languages created in the last 30 years or so? Because e.g. in Rust what you're talking about is: #[derive(Deserialize)] struct Whatever { id: usize, first_name: String } // ... let thing: Whatever = get(some_url)?.json()?; You encode your assumptions as a structure, then you do your call, ask for the conversion, get notified that that can fail (and you can get an error if you but ask), and then you get your value knowing your assumptions have been confirmed. It's definitely less "throw at the wall" than just thing = get(some_url).json() but it's a lot easier to manipulate, inspect, and debug when it invariably goes to shit because some of your assumptions were wrong (turns out `first_name` can be both missing and null, for different reasons). For a 5 lines throwaway script the latter is fine, but if it's going to run more than a pair of time or need more than few dozen minutes (to write or run) I've always been happier with the former, the cycle of "edit, make a typo, breaks at runtime, fix that, an other typo, breaks again, run for an hour, blow up because of a typo in the reporting so you get jack shit so you get to run it again".
- lisper 4y agoBecause you haven't finished the whole thing but you want to test part of it before you continue.
- iaabtpbtpnn 4y agoThere's always an escape hatch for this. Even in Java (and Haskell), you can just throw an unchecked exception from your unimplemented function.
- josephg 4y agoLikewise in Rust you can todo!() in incomplete functions. But it’s still much harder to run incomplete code than it is in javascript, because you still need to satisfy the type checker and borrow checker even for incomplete programs.
- lliamander 4y agoThere are two reasons you may want to run a program that fails typechecking: 1. The program is in fact correct, but the typechecker can't prove the program is correct and so reports it as a failure (for contrast other typecheckers only report errors if it can prove that there is an error) 2. The type checker is correct in that the program is flawed in some way, but you are the middle of development and it would be more productive if you could observe the behavior of the program before fixing the types. Since your CI pipeline will fail on type errors it's not like you're going to be able to merge your changes before fixing them.
- cjglo 4y ago
- ReflectedImage 4y agoLet me add a 3rd. 3. The program is incorrect but correct for all expected end user inputs.
- naasking 4y agoIn that case, add a type that constrains the values to the expected user inputs. Now not only are you passing the type checking, you're strongly enforced this invariant at the point where it makes the most sense, you've future-proofed this code against possible future misuse where the expected inputs might change, and you've made the code essentially self-documenting.
- lliamander 4y agoThat is the correct end state, but there can be practical considerations for why you might want to run (and possibly even release) the software in this partially correct state, and fix the edge cases later.
- naasking 4y agoI don't disagree, but most statically typed languages don't enforce a level of correctness where this would be an issue. We're not programming with theorem provers with an elaborate specification. I somewhat agree with your point #2 which is why Haskell provides a deferred type checking mode for prototyping purposes[1]. I think this should be more common among typed languages, but this is not a very typical scenario either. [1] https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/defer_type_errors.html https://downloads.haskell.org/ghc/latest/docs/users_guide/ex...
- hbrn 4y agoWhy would you want to talk to people who are wrong? Sometimes I don't care which political party the barista voted for, I just want to get my coffee. People and programs are never perfect. Type checked program is not guaranteed to be correct, it just has stronger constraints.