8 ms·
Pretty much the first sentence on the page: > No runtime exceptions, ever. Every bit of Grain you write is thoroughly sifted for type errors, with no need for
by rzwitserloot 8y ago
Pretty much the first sentence on the page:
> No runtime exceptions, ever. Every bit of Grain you write is thoroughly sifted for type errors, with no need for any type annotations.
That's... weird to me. That seems to posit that ALL runtime exceptions are necessarily type errors. Huh?
What about full disk, DB errors, data verification error, parsing errors, network errors, and tons of other errors that don't appear to be type system related?
[A] This language only doesn't realize this class of errors exist, which, um, seriously? That can't bode well.
[B] The language uses the type system to propagate such errors. `Either<Result, ErrorCode>` for example. The language is somewhat unique in that it is designed to enforce such code style for ALL possible error conditions.
[C] The language returns null or blank objects on error conditions, such as how (early) javascript returns 'NaN' when trying to parse "hello!" as a number, instead of raising an error condition. That's a style choice I really wouldn't like, but I guess to each their own.
- leshow 8y agoThe likely reason is B. Elm is a frontend web language that also has this guarantee, and it uses Either types for situations that can fail. It's not entirely unique in that regard, there are other languages (mostly) without exceptions; Rust, for example.
- mml 8y agoElm goes even further, insisting that you actually handle the Error types in some way.
- colanderman 8y agoI would assume case B, which isn't really that unique. Erlang for example follows that pattern (and has no concept of "exception"). After all, I/O errors, parsing errors, etc., aren't exceptional; they very much should be expected. Forcing them to be handled in the normal code path helps create robust systems. (There are of course also true runtime errors -- e.g. out-of-memory -- and precondition errors -- i.e., coding errors that fall outside the capability of the type system to enforce -- which do not fit this pattern.)
- macintux 8y ago>> The language uses the type system to propagate such errors. ... > Erlang for example follows that pattern. Erlang is its own beast; if you characterize pattern matching as part of a type system, which I guess it is, then...maybe if you squint? Normal errors are indeed typically reflected in the return value, but if the error pattern doesn't match the code's expectations then a runtime exception is thrown...if you're lucky. If the error pattern happens to fit with the pattern match but the code doesn't know about it, eventually some other error will occur. Anyway, Erlang definitely doesn't fall into the "No runtime exceptions, ever" category, and as such doesn't really fit with the options the OP presented.
- colanderman 8y agoI think we read the OP differently. I'm just responding to the bit about errors being returned as an "Either" type. You are right that Erlang does not enforce pattern matching to succeed, though its static analyzer (Dialyzer) can catch many instances when it definitely won't.
- dragonwriter 8y ago> Normal errors are indeed typically reflected in the return value, but if the error pattern doesn't match the code's expectations then a runtime exception is thrown With a sufficiently complete static (even inferred) type system, you can catch any possibility of such failures AOT and not run into any error patterns that don't match code expectations at runtime, so while the description isn't literally true of Erlang, Erlang does show a way that it could be true of a language with AOT enforcement of adequate type system.
- masklinn 8y ago> Erlang for example follows that pattern (and has no concept of "exception"). Erlang absolutely has a concept of exceptions. They're even called exceptions: http://erlang.org/doc/reference_manual/errors.html#exceptions http://erlang.org/doc/reference_manual/errors.html#exception....
- 8y ago
- tomp 8y agoThis is actually a really complex problem. Recently in C++, the discussion is happening regarding `nothrow` functions. Can such functions allocate memory (and hence throw "out-of-memory" errors)? The basic tradeoff is, most program can't (or don't) handle an "out-of-memory" error anyways, so for those, "nothrow" and "nothrow+allocates" is essentially the same. On the other hand, some (few) software is written to handle "out-of-memory" errors, and C++ is one of the few languages that targets such software, so in those programs, the distinction is meaningful.
- atombender 8y agoErrors could presumably be provided as return values (similar to Rust's Option or Haskell's Either), though we know from languages like Go that propagating these manually is a chore and an eyesore. A harder problem is errors that cannot be handled by a simpler type system. For example, consider Haskell's head function, which is defined like this: head :: [a] -> a In other words, it takes a list, and returns the first element. But if the list is empty, it throws an exception (technically called an "error" in Haskell): > head [] *** Exception: Prelude.head: empty list Haskell's type system is extremely powerful, but it cannot catch this at compile time. In order to express constraints such as "list must not be empty" or "number must be higher than 1" or "file must be open, the type system needs to understand the semantics of the values expressed by a type system. This can be solved by dependent typing, but that's a complicated concept currently implemented by only a handful of languages (e.g. Idris).
- tylerhou 8y agoYou still can't avoid runtime errors because of non-terminating functions. Determining whether an arbitrary function in a Turing complete language is total or not is undecidable. I don't see any mention of a totality checker (which would make the language not Turing complete) either. Idris also has this.
- akegalj 8y ago> This can be solved by dependent typing dependant typing isn't needed for some use cases you have mentioned: > list must not be empty you can use NonEmpty https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-List-NonEmpty.html https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-L... . We are still encoding this special case and our intent in types. Cons is that we need a special support for it (special library) where with dependant types we could reason about this use case without a special library EDIT: added newlines
- masklinn 8y ago> Errors could presumably be provided as return values (similar to Rust's Option or Haskell's Either), though we know from languages like Go that propagating these manually is a chore and an eyesore. Mostly because Go is terrible. > Haskell's type system is extremely powerful, but it cannot catch this at compile time. The problem is not the type system, it's that Haskell's error handling is inconsistent. Elm, despite having a much simpler type system than Haskell, handles this properly: head : List a -> Maybe a This has nothing to do with the type system itself and everything to do with how the type system is used (or not used). The designers of Haskell's `head` decided to make it a partial function, leading to type-checking inputs not generating any output.
- oscarspen 8y agoGrain dev here! Fixed that on the site. That line was just supposed to be about runtime type errors, not all errors. Grain is definitely still really in its alpha form. There's lots we want to do with it that we're still getting around to! We weren't quite expecting much of any traffic to the site, but that's really my fault for having the site be public before we were at least a bit further along. :) Even still, it's been awesome to get a lot of early feedback! We'll get a roadmap together so it's easier for people to see where we're trying to take the language.