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
tmpfilein format instead of other way around, valuesvideo (tmpfile "test.avi")etc. - make lots of separate data types
videotmpfile,picturetmpfileetc. - make
tmpfiletypeclass - 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
Post a Comment