[Haskell-beginners] Help using Type Families

MAN elviotoccalino at gmail.com
Sat Jul 31 14:21:52 EDT 2010


Thank you, Antoine! I hadn't picked up on that. It worked perfectly.
You are right about this solution being too complex, too. It is. But I
admit to be more stubborn than allowed :D

El vie, 30-07-2010 a las 22:05 -0500, Antoine Latter escribió:
> On Fri, Jul 30, 2010 at 5:06 PM, MAN <elviotoccalino at gmail.com> wrote:
> > I desperately need help with this little predicament I got myself into.
> > I thought It'd be nice to write a piece of code that would wrap around
> > an older function, which given a bunch of parameters produces a plot (a
> > file) in the working directory. This new code receives the parameters
> > first, and only if the plot is not already present in the directory,
> > should pass the parameters to (and run) the wrapped function.
> >
> > With that in mind I've defined the following type class. The older
> > function is treated as an anonymous 'muscle', capable of taking its
> > parameters ordered in some structure unknown.
> >
> >> class Show parType => MuscleCapable parType where
> >>   -- | This could be a list or a tuple, etc. Whatever way of
> > implementing it, it
> >>   --   contains all necessary parameters to make the muscle produce a
> > resource.
> >>   --   Notice that this definition forces the type of ALL the
> > parameters of
> >>   --   the wrapped up muscle to be the same.
> >>   data Parameters parType
> >>
> >>   -- | A way to obtain the values of a @Parameters a@, for printing
> > purposes
> >>   listOfParams :: Parameters parType -> [parType]
> >>
> >>   -- | This wrapper around the muscle function will allow me to
> > produce a
> >>   --   resource on demand.
> >>   muscle :: Parameters parType -> IO FilePath
> >
> > But when trying to use that, I get different errors at compile time
> > related to my (mis)use of type families, most of which are above my
> > head.
> > I use this dummy code to test the mechanism:
> >
> >> data ThreeTuple a = T3 a a a
> >>
> >> listFromThreeTuple :: ThreeTuple a -> [a]
> >> listFromThreeTuple (T3 x y z) = [x,y,z]
> >>
> >> fun (T3 x y z) = do
> >>   withFile "output.out" WriteMode $ flip hPutStrLn (concatMap show
> > [x,y,z])
> >>   return "output.out"
> >>
> >> instance MuscleCapable Int where
> >>   data Parameters Int = ThreeTuple Int
> >>   listOfParams = listFromThreeTuple
> >>   muscle = fun
> >
> > This fires the following error in GHC (I do add the TypeFamilies
> > LANGUAGE pragma):
> > """
> >    Couldn't match expected type `Parameters Int'
> >           against inferred type `ThreeTuple Int'
> >      NB: `Parameters' is a type function
> > """
> >
> > If I use a similar data type without type parameters:
> >
> >> data ThreeTuple = T3 Int Int Int
> >
> > And modify the above snippet accordingly, I get the same thing:
> > """
> >    Couldn't match expected type `Parameters Int'
> >           against inferred type `ThreeTuple'
> >      NB: `Parameters' is a type function
> >    In the expression: listFromThreeTuple
> >    In the definition of `listOfParams':
> > """
> >
> > Whatever alternative I try (and I have gone far along the trial-error
> > path) inevitably yields those errors for both lines implementing
> > 'listOfParams' and 'muscle'.
> >
> > Could somebody help me understand what's going on? I'm really lost at
> > this point.
> >
> >
> > PS: My first attempt at the class used 'type' to define the associated
> > type. I had to switch to 'data' due to problems regarding the chosen
> > type not being _injective_. GHC error messages and the paper "Fun with
> > type functions" helped on that.
> >
> 
> The error is with your data instance declaration:
> 
> > data Parameters Int = ThreeTuple Int
> 
> You're treating a data declaration as if it were a type declaration -
> a data declaration needs to have a constructor, something like:
> 
> > data Paramter Int = ParamInt (ThreeTuple Int)
> 
> would be more likely to type check.
> 
> Then you'd have to pattern match on the constructor to deconstruct it.
> 
> 
> Type errors aside, all of this looks really really complicated, and
> from what you're describing I'm not sure it needs to be.
> 
> Why not just have two functions, one which wraps around the first? Or
> if you want to write code in a reusable manner, ordinary higher-order
> functions work well:
> 
> > writeToFileIfNonExistent :: FilePath -> (FilePath -> IO ()) -> IO ()
> > writeToFileIfNonExistent path action
> >  = do
> >   exists <- doesFileExist path
> >   unless exists $ action path
> >   return ()
> 
> > coreFileWritingFunction :: Param1 -> Param2 -> Param3 -> FilePath -> IO ()
> > coreFileWritingFunction p1 p2 p3 path = withFile path WriteMode $ \handle -> ...
> 
> > slickNewWrapper :: Param1 -> Param2 -> Param3 -> FilePath -> IO ()
> > slickNewWrapper p1 p2 p3 = writeToFileIfNonExistent $ coreFileWritingFunction p1 p2 p3
> 
> I've chosen silly names for my functions, but I hope you get the idea.
> Don't hesitate to respond if you have any questions.
> 
> Antoine




More information about the Beginners mailing list