4 ms·
The requirement from the post is for a non-empty list of file paths - your Cache representation only contains a single item so is obviously not suitable. In cas
by lkitching 5y ago
The requirement from the post is for a non-empty list of file paths - your Cache representation only contains a single item so is obviously not suitable. In case it's not obvious: head is not the only operation that might be required and the author isn't using (NonEmpty a) as a wrapper around a single value. The requirements for the configuration are stronger than those provided from the input and the configuration type used by the program encodes that property in the type. That property is then enforced globally throughout the entire program and only needs to be checked once at the top level.
> The above is not best-understood as a "parser". The above is validating the input
Yes the example you gave is an example of validating in the author's formulation because it does not enforce the property it's checking in the return type. You check the list is non-empty but this information is not available anywhere else in the program. A parser would return a (NonEmpty String) as its result since that does enforce the constraint.
> Trying to redefine "parsing" to mean "the result has a different return type"
That's not a redefinition of parsing, that's what a parser is.
- kingdomcome50 5y ago> The requirement[0] from the post is for a non-empty list of file paths I'm sure you could imagine my example record containing more keys no? The requirements from the post are arbitrary and could (should) be anything that best-illustrates the thesis of the post. For example by choosing a representation of their "configuration" that suffers from this silly problem of containing unknown content after parsing, the author introduces the whole `NonEmpty` gymnastics. It's totally avoidable. The irony is that the author was so close to getting it right! > You check the list is non-empty but this information is not available anywhere else in the program The function in my example does statically define that the returned list is non-empty. A "parser" would maybe return the non-empty list if parsing was successful. That's how parsers work. [0] The requirement from the post is, in fact, to only have a single file path. That is the only data actually being used (i.e. required). The other intermediate data structures are a choice of the author.
- lkitching 5y ago> The requirements from the post are arbitrary and could (should) be anything that best-illustrates the thesis of the post The post is showing how using more expressive types can simplify your code through a very simple example. You started out saying the NonEmpty type is unnecessary and now you're complaining it's not complicated enough, so it's not clear what your actual objection is here. > The function in my example does statically define that the returned list is non-empty The type given in your example was getConfDirs :: Maybe [FilePath] this is inhabited by (Just []) so no it doesn't statically guarantee that. > A "parser" would maybe return the non-empty list if parsing was successful That's exactly what the nonEmpty function in the post does: nonEmpty :: [a] -> Maybe (NonEmpty a)
- kingdomcome50 5y ago> The post is showing how using more expressive types can simplify your code This is called "using a type system" and has nothing to do with parsing or validation. You just don't "get it" I suppose. I feel like we are talking right past each other. You are so hung up on the types that you are just failing to take in the essence of what's going on. I'll say this one more time: the specific type returned by `getConfDirs` is completely irrelevant -- other than whether it is wrapped in `Maybe` or not (because this best-illustrates parsing vs. validation). The returned type is an implementation detail that is chosen by the author. It is not necessary or "required" that we parse a string into a list (read that again). It's really quite simple: // this is parsing getConfDirs :: Maybe Config // this is validation getConfDirs :: Config // might throw How these functions are implemented is not relevant. The irony is that choosing a list representation makes for a good example of parsing precisely because we can't know how many elements are in the list. That is, it gives the author the opportunity to further-illustrate parsing by: // more parsing maybeCacheDir <- (confDirs >>= first) // or second, third, fourth, etc. The author's example code is NOT illustrating parsing. Period. They are essentially illustrating a constructor named `getConfigurationDirectories` -- which is the most classic case of validation imaginable (TS): // 1. This is their first example class ConfigurationDirectories { dirs: FilePath[]; constructor(env) { let dirs = env("config").split(","); if (dirs.length < 1) throw "Error!"; this.dirs = dirs; } get cacheDir() { if (this.dirs.length < 1) throw "Cannot happen!"; return this.dirs[0]; } } // 2. This is their "fixed" example with all sorts of unnecessary "NonEmpty" gymnastics because they have chosen the wrong abstraction type NonEmpty<T> = [T, ...T[]]; const eg: NonEmpty<string> = []; // error class ConfigurationDirectories { dirs: NonEmpty<FilePath>; constructor(env) { let dirs = env("config").split(","); if (dirs.length < 1) throw "Error!"; this.dirs = dirs as NonEmpty<FilePath>; } get cacheDir() { // now the compiler also knows dirs is not empty return this.dirs[0]; } } // 3. This is BETTER than their "fixed" example because it is even simpler class ConfigurationDirectories { cacheDir: FilePath; constructor(env) { let dirs = env("config").split(","); if (dirs.length < 1) throw "Error!"; this.cacheDir = dirs[0]; } } // usage const config = new ConfigurationDirectories(getEnv()); const cache = initializeCache(config.cacheDir); You see how the above translates to the author's code? Nothing above qualifies as "parsing". Their examples are defining a function with a guard that validates the input. The author then get confused and decides to go on this side-quest of how to trick the compiler because the first function wasn't actually accomplishing their goal (they have to validate twice!). But you know what would have avoided all of it? Parsing (i.e. actually using the types to simplify their code). Simple. Linear. Fail-safe. Parsing. I'm not really sure why you are dying on this hill. You can't "win" this argument. The best you can accomplish is to learn something yourself through this discussion. It's just a matter of fact that the author is (mis)using `Maybe` to the detriment of their examples. And that isn't an attack on the author! You needn't defend them. We've all written code that wasn't perfect. This is just another instance. Maybe if the title were something like, "How to validate with static guarantees in Haskell" I wouldn't have said anything... * I thought you were referring to my TS example (which does statically define the return-type to be non-empty)