[Haskell-cafe] Declaring Functors for Type constrained data types

Patrick Chilton chpatrick at gmail.com
Wed Mar 1 13:24:05 UTC 2017


>
>  Is that thinking usually an anti-pattern.


Typeclasses choose between implementations of a method based on static
*types* known at compile time. In OOP, which implementation of an interface
method you get depends on the run-time object *instance*. They're not
really related despite the similarities.

For example, if you have a list of widgets and you want to do something
different depending on what their run-time value is, a typeclass would be
the wrong thing to use since it dispatches on types, not values. It's
possible to make them work a little bit like OOP classes, but then you end
up with the classic antipattern.

On Wed, Mar 1, 2017 at 2:16 PM, Guru Devanla <gurudev.devanla at gmail.com>
wrote:

> This problem I was solving is more of a re-implementation of some code  I
> had in Python. In Python, I had a very class-based structure of this
> design. By class based structure, I mean we have class-level methods for
> each class (where each class  is a type of Task), and all of them implement
> the same set of methods. This classes are never instantiated, but all
> methods are invoked on the class.
>
> WIth that perspective, the record patter @Patrick recommended directly
> maps to that design. The record data type here becomes a
> abstract-representation of my class(abstract base class in OO terms)  and
> each task provides its methods.  I see that relationship.  Is that the
> approach I should be aiming for?
>
> Secondly, while choosing the type-class approach,  I imagined all these
> required class methods to be an interface and therefore an interface could
> directly map to a type class in Haskell.  Is that thinking usually an
> anti-pattern.
>
> The existential antipattern
> <https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/>.
> link was very useful and made me re-asses my inclination to defining type
> classes and embedded types right away.
>
> On Tue, Feb 28, 2017 at 9:55 AM, Patrick Chilton <chpatrick at gmail.com>
> wrote:
>
>> You could also consider representing tasks like this instead of using a
>> typeclass:
>>
>> data Task = Task
>>   { process :: m ()
>>   , canRun :: m Bool
>>   }
>>
>> The Taskable + existential GADT example seems like it could be an example
>> of the existential antipattern
>> <https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/>
>> .
>>
>> If your GADT really does have a as a type parameter, it would be more
>> idiomatic to check for the typeclass when you use it:
>>
>> doStuffWithTasks :: Taskable a => Task a -> ...
>>
>> But then what's the point of the Task datatype?
>>
>> On Tue, Feb 28, 2017 at 1:48 AM, Guru Devanla <gurudev.devanla at gmail.com>
>> wrote:
>>
>>> Hello All,
>>>
>>> I am working on a program that will define a bunch of tasks. Each task
>>> will have to implement certain methods as part of a type class.
>>>
>>> -- task 1
>>> data UpdateAcctsTask = UpdateAccts
>>>
>>> -- task 2
>>> data EmailConfig = EmaiConfig {someattrs::String}
>>> data SendEmailTask = SendEmailsTask EmailConfig
>>>
>>> -- task 3
>>> data GeneralWriterTask a = GeneralWriterTask a
>>>
>>> Each of these tasks implement a class, Taskable. The return
>>> values are simplified for this example.
>>>
>>> class Taskable a where
>>>   process :: a -> Bool
>>>   can_run :: a -> Bool
>>>
>>>
>>> This works fine. I can expand on these tasks and execute them.
>>>
>>> Now, I wanted to be able to defined dependencies between these
>>> (Taskable's). I decided
>>> I could create a data type for this dependency and may be also get a
>>> FreeMonad
>>> around this structure for further processing using a graph of Tasks.
>>> But, before that I wanted
>>> to create an wrapper for these Taskables and create a functor for it as
>>> follows
>>>
>>> The first thing I did was, define a Task, which generalizes over all
>>> the above defined (and future Taskables)
>>>
>>> data Task a where
>>>   Task :: (Taskable a) => a -> Task a
>>>
>>>
>>> instance Functor Task where
>>>   fmap:: (Taskable a, Taskable b) -> (a -> b) -> Task a  -> Task b
>>> --- THIS DOES NOT WORK
>>>   fmap f (Task a) = Task $ f a
>>>
>>>
>>> But, I realized that I cannot define an fmap over a type constraint.
>>>
>>> My questions are:
>>>
>>> 1. Is there any way to do this. I see there is an answer of SO. I wanted
>>>    to make sure if there were any improvements to this since that answer'
>>>    was posted.
>>>    http://stackoverflow.com/questions/17157579/functor-instance
>>> -for-a-gadt-with-type-constraint
>>>
>>> 2. Secondly, I would like to know why this is not possible. Is it a
>>> current
>>>    limitation of GHC or if there is some fundamental category theory
>>> concepts
>>>    that dis-allows such declarations that I need to grok!
>>>
>>> Appreciate any help on this. Thank you!
>>>
>>> _______________________________________________
>>> Haskell-Cafe mailing list
>>> To (un)subscribe, modify options or view archives go to:
>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>>> Only members subscribed via the mailman list are allowed to post.
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20170301/e7ab174f/attachment.html>


More information about the Haskell-Cafe mailing list