In Haskell, how do you restrict functions to only one constructor of a data type? -


i'm not sure how word question. i'm trying pass paths of tmpfiles around, , want capture idea there different formats of tmpfile, , each function works on 1 of them. works:

data fileformat   = spreadsheet   | picture   | video   deriving show  data tmpfile = tmpfile fileformat filepath   deriving show  videopath :: tmpfile -> filepath videopath (tmpfile video p) = p videopath _ = error "only works on videos!" 

but there must better way write without runtime errors right? thought of 2 alternatives, this:

type tmpspreadsheet = tmpfile spreadsheet type tmppicture     = tmpfile picture type tmpvideo       = tmpfile video  videopath :: tmpvideo -> filepath 

or this:

data tmpfile = tmpfile filepath   deriving show  videopath :: tmpfile video -> filepath 

but don't compile. what's proper way it? other ideas, none particularly appealing:

  • wrap tmpfile in format instead of other way around, values video (tmpfile "test.avi") etc.
  • make lots of separate data types videotmpfile, picturetmpfile etc.
  • make tmpfile typeclass
  • use partial functions everywhere, add guard functions abstract pattern matching

i considered learning -xdatakinds extension, suspect i'm missing simpler can done without it.

edit: i'm learning lot today! tried both approaches outlined below (datakinds , phantom types, have dummy value constructors can removed extension), , both work! tried go little further. both let make nested type tmpfile (listof a) in addition regular tmpfile a, cool. i've tentatively decided go plain phantom types (intact value constructors), because can pattern match on them. example, surprised works:

data spreadsheet = spreadsheet deriving show data picture     = picture     deriving show data video       = video       deriving show data listof    = listof    deriving show  data tmpfile = tmpfile filepath   deriving show  videopath :: tmpfile video -> filepath videopath (tmpfile video p) = p  -- read file contains list of filenames of type a, -- , return them individual typed tmpfiles listfiles :: tmpfile (listof a) -> io [tmpfile a] listfiles (tmpfile (listof fmt) path) =   txt <- readfile path   let paths = map (tmpfile fmt) (lines txt)   return paths  vidpath :: tmpfile video vidpath = tmpfile video "video1.txt"  -- $ cat videos.txt -- video1.avi -- video2.avi vidslist :: tmpfile (listof video) vidslist = tmpfile (listof video) "videos.txt"  main :: io [filepath] main =   paths <- listfiles vidslist  -- [tmpfile video "video1.avi",tmpfile video "video2.avi"]   return $ map videopath paths -- ["video1.avi","video2.avi"] 

as far can tell, equivalent datakinds similar, can't access fmt value:

{-# language datakinds, kindsignatures #-}  data fileformat   = spreadsheet   | picture   | video   | listof fileformat   deriving show  data tmpfile (a :: fileformat) = tmpfile filepath   deriving show  vidpath :: tmpfile video vidpath = tmpfile "video.avi"  vidslist :: tmpfile (listof video) vidslist = tmpfile "videos.txt"  videopath :: tmpfile video -> filepath videopath (tmpfile p) = p  listfiles :: tmpfile (listof a) -> io [tmpfile a] listfiles (tmpfile path) =   txt <- readfile path   let paths = map tmpfile (lines txt)   return paths  main :: io [filepath] main =   paths <- listfiles vidslist   return $ map videopath paths 

(it may seem weird thing want, actual program going interpreter small language compiles shake rules tmpfile corresponding each variable, typed lists of tmpfiles useful)

does seem right? idea of datakinds better, go instead if inspect them values, or if turns out that's never needed.

you're right: -xdatakinds, tmpfile video -> filepath approach work. , indeed think may application extension.

{-# language datakinds #-}  data tmpfile (a :: fileformat) = tmpfile filepath   deriving show  videopath :: tmpfile video -> filepath 

the reason need extension write tmpfile video constructors of fileformat ab initio value-level (thus exist @ runtime), while tmpfile type-level / compile-time.

of course there's way generate type-level entities: define types!

data spreadsheet = spreadsheet data picture = picture data video = video  data tmpfile = tmpfile filepath   deriving show  videopath :: tmpfile video -> filepath 

such types called phantom types. really, they're bit of hack work around former lack of proper type-level values, datakinds has given us. so, unless need compatibility old compilers, use datakinds!

an alternative not enforce file type @ compile time, make explicit functions partial.

data tmpfile = tmpfile fileformat filepath   deriving show  videopath :: tmpfile -> maybe filepath videopath (tmpfile video p) = p videopath _ = nothing 

in fact, approach might more rational one, depending on you're planning do.


Comments

Popular posts from this blog

java - pagination of xlsx file to XSSFworkbook using apache POI -

Unlimited choices in BASH case statement -

apache - How do I stop my index.php being run twice for every user -