haskell - Polymorphic types in records -
i'm trying write function reads raw bytes file, "casts" "plain" type, sorts it.
in order this, need tell sort how should interpret binary data - i.e., type of binary data is.
in order "binary" data, in sense of "i can treat data raw bits, read , write disk", type of data must binary , bits. and, sort it, must member of ord.
any type constrained these ways should sortable.
as little hack, in order pass type sort function, passing inhabitant of type instead. (if there's way pass type , achieve results, i'd love know.)
{-# language rankntypes #-} import data.binary.get import data.binary.put type sortable = forall a. (bits a, binary a, ord a) => data sortopts = sortopts { maxfiles :: int , maxmemory :: integer , maxthreads :: int , bintype :: sortable } defaultopts = sortopts { maxfiles = 128 , maxmemory = 1000 * 1000 * 1000 * 1000 , maxthreads = 4 , bintype = 0 :: word32 }; putbinaryvalues :: binary => handle -> [a] -> io () putbinaryvalues out vals = let bytes = runput . mapm_ put $ vals bl.hput out bytes binaryvalues :: (binary a, bits a) => -> handle -> io [a] binaryvalues t inf = size <- hfilesize inf let cast = runget (genericreplicatem (size `div` bytewidth) get) cast . bl.fromchunks . (:[]) <$> bs.hgetcontents inf genericreplicatem n = sequence . (dl.genericreplicate n) bytewidth = fromintegral $ (bitsize t) `div` 8
but doesn't compile. apparently haskell insists of values of record concrete types. @ least, that's i'm gathering error message:
could not deduce (a ~ word32) context (bits a, ord a, binary a) bound type expected context: (bits a, ord a, binary a) => @ ... `a' rigid type variable bound type expected context: (bits a, ord a, binary a) =>
so, how could achieve generalization?
edit:
i wanted use record update syntax "configure" sort. e.g.:
configure = defaultopts -- , exporting
and later
let myopts = configure{ bintype = 42 :: word16 }
but doesn't work, , can't quite understand why, unless it's nyi.
record update insufficiently polymorphic field: bintype :: in expression: configure {bintype = words !! 0} in equation `o': o = configure {bintype = words !! 0} in expression: { intesthandle <- intest; words <- testrandomwords; putbinaryvalues intesthandle $ take 100 words; seekbeg intesthandle; .... }
so, client code have copy values out of defaultopts piecemeal , make new record every wants reconfigure sort?
you can use existentialquantification
in sortopts
type. following compiles:
{-# language existentialquantification #-} import data.bits import data.word import data.binary import data.binary.get import data.binary.put data sortopts = forall a. (bits a, binary a, ord a) => sortopts { maxfiles :: int , maxmemory :: integer , maxthreads :: int , bintype :: } defaultopts = sortopts { maxfiles = 128 , maxmemory = 1000 * 1000 * 1000 * 1000 , maxthreads = 4 , bintype = 0 :: word32 }
however, note cannot use bintype
function because have type exists a. sortopts -> a
, cannot existential types return values. can field value pattern matching, example
test :: sortopts -> bytestring -> bytestring -> ordering test (sortopts{bintype=bintype}) bsa bsb = compare b = runget bsa `astypeof` bintype b = runget bsb `astypeof` bintype
this deserializes , compares 2 bytestrings using existential bintype
in given sortopts
.
as noticed, haskell's record-update syntax doesn't support existential fields, need update bintype
:
defaultopts = sortopts { maxfiles = 128 , maxmemory = 1000 * 1000 * 1000 * 1000 , maxthreads = 4 , bintype = 0 :: word32 } alternativeopts = withbintype (0 :: word16) $ defaultopts { maxfiles = 256 } withbintype :: (bits a, binary a, ord a) => -> sortopts -> sortopts withbintype bt (sortopts{..}) = sortopts maxfiles maxmemory maxthreads bt
the above uses recordwildcards
make copying record bit easier. it's handy extension when using options record later on.
alternatively, jozefg suggested, use wrapper type bintype
. use this:
{-# language existentialquantification #-} data bintype = forall a. (bits a, binary a, ord a) => bintype data sortopts = sortopts { maxfiles :: int , maxmemory :: integer , maxthreads :: int , bintype :: bintype } defaultopts = sortopts { maxfiles = 128 , maxmemory = 1000 * 1000 * 1000 * 1000 , maxthreads = 4 , bintype = bintype (0 :: word32) } alternativeopts = defaultopts { bintype = bintype (0 :: word16) }
since sortopts
regular record type can use record operations normally. refer unwrapped bintype
, need pattern match on wrapper test
example before become (using recordwildcards
)
test :: sortopts -> bytestring -> bytestring -> ordering test (sortopts{..}) bsa bsb = case bintype of bintype bt -> compare b = runget bsa `astypeof` bt b = runget bsb `astypeof` bt
note of above assumes have particular use-case need able hide exact type parameter behind existential reason. normally, leave type-parameter in sortopts
, constrain in functions use sortopts
. i.e.
data sortopts = sortopts { maxfiles :: int , maxmemory :: integer , maxthreads :: int , bintype :: } test :: (bits a, binary a, ord a) => sortopts -> bytestring -> bytestring -> ordering test (sortopts{..}) bsa bsb = compare b = runget bsa `astypeof` bintype b = runget bsb `astypeof` bintype
you can use constraintkinds
extension make shorter alias if needed, in
{-# language constraintkinds #-} type bintype = (bits a, binary a, ord a) test :: bintype => sortopts -> bytestring -> bytestring -> ordering
Comments
Post a Comment