
Hi, In order to get a feel for Haskell, I decided to write a simple CRUD application. I stumbled upon phantom types while looking for ways to validate data in Haskell. Here is what I have done so far: {-# OPTIONS_GHC -XTemplateHaskell #-} module Foo.Entities(buildTask, Sanitise, sanitise, Task, Validated, Unvalidated, name) where import Data.Lens.Lazy ( (~=), access, (%=), mapLens, (^=), (^.), (^%=), (^%%=), (^$) ) import Data.Lens.Template ( makeLenses ) data Validated data Unvalidated data Task a = Task { _name :: String } makeLenses [''Task ] buildTask :: String -> Task Unvalidated buildTask name = Task { _name = name } class Sanitise a where sanitise :: Task a -> Either (Task Validated) String instance Sanitise Validated where sanitise t = Left t instance Sanitise Unvalidated where sanitise t | t ^. name == "" = Right "Error: the name field should be not be empty" | otherwise = Left (Task { _name = t ^. name } ) Is this the recommended way of doing something like this? Do I really need to export each lens I'm using? When I'm exporting "Task", I seem to be exporting the data type, but is there a way to export the Task constructor itself (not that I would want that)? Also, how would I go about generalizing my Sanitise typeclass, so that it would work on other data types than Task? Of course, it's quite possible that I'm reinventing the wheel and that I've missed a perfectly useable data validation module on hackage. Thanks, Emm

On Sat, Oct 20, 2012 at 12:41 AM, Emmanuel Surleau
Is this the recommended way of doing something like this?
It looks to me like using a "smart constructor" would be sufficient in this case. That is, you have a function: makeTask :: String -> Either String Task This function performs your validation. Then you make sure not to export the data constructor for Task - that way, the only way to make a Task outside of this module is to call the validating constructor function.
When I'm exporting "Task", I seem to be exporting the data type, but is there a way to export the Task constructor itself (not that I would want that)?
You can say Foo(..) to export all the data constructors of type Foo. Or, you can list out the data constructors you want to export inside the parens. If you want to be explicit about the fact that you are not exporting any data constructors, you should probably write Foo(). Note that the same syntax applies to classes and their methods.
Of course, it's quite possible that I'm reinventing the wheel and that I've missed a perfectly useable data validation module on hackage.
I don't know of any. But I'm also not convinced that such a thing is necessary - smart constructors are usually all you need. Perhaps in a context with more assumptions (i.e., the web), there would be some useful work that a validation library could do. -Karl

Karl Voelker
On Sat, Oct 20, 2012 at 12:41 AM, Emmanuel Surleau
wrote: Is this the recommended way of doing something like this?
It looks to me like using a "smart constructor" would be sufficient in this case. That is, you have a function:
makeTask :: String -> Either String Task
This function performs your validation. Then you make sure not to export the data constructor for Task - that way, the only way to make a Task outside of this module is to call the validating constructor function.
l see. However, with phantom types, you get more flexibility. For instance, you can generate a blank Task as a template. I think I want to retain the ability to work with non-well-formed data. But I'll keep smart constructors in mind.
When I'm exporting "Task", I seem to be exporting the data type, but is there a way to export the Task constructor itself (not that I would want that)?
You can say Foo(..) to export all the data constructors of type Foo. Or, you can list out the data constructors you want to export inside the parens. If you want to be explicit about the fact that you are not exporting any data constructors, you should probably write Foo().
Note that the same syntax applies to classes and their methods.
Thanks. This means I can export Sanitise (..) instead of Sanitise and sanitise.
Of course, it's quite possible that I'm reinventing the wheel and that I've missed a perfectly useable data validation module on hackage.
I don't know of any. But I'm also not convinced that such a thing is necessary - smart constructors are usually all you need. Perhaps in a context with more assumptions (i.e., the web), there would be some useful work that a validation library could do.
I guess you are right. Not many use cases for a completely generic validation library. Thanks, Emm -- Sent from my Android phone with K-9 Mail. Please excuse my brevity.
participants (2)
-
Emmanuel Surleau
-
Karl Voelker