[Haskell-cafe] Library API design: functional objects VS type classes
Joey Adams
joeyadams3.14159 at gmail.com
Tue Mar 5 21:47:10 CET 2013
On Mon, Mar 4, 2013 at 5:50 PM, Rob Stewart <robstewart57 at gmail.com> wrote:
> ...
> -----
> import Control.Concurrent
>
> -- API approach 1: Using type classes
> class FooC a where
> mkFooC :: IO a
> readFooC :: a -> IO Int
> incrFooC :: a -> IO ()
>
I recommend taking 'mkFooC' out of the typeclass. It keeps you from being
able to (easily) construct a 'FooC' from dynamic data, e.g.:
mkFoo :: Host -> Port -> IO MyFoo
After this change, the typeclass approach and the data constructor approach
are nearly equivalent, except:
* With the typeclass approach, the compiler passes the dictionary
implicitly, which can be more convenient to use (e.g. `readFooC a` instead
of `readFooC (getFoo a)`).
* With the typeclass approach, you have to define a Foo type to contain
the environment needed for Foo methods. With the record approach, you can
just construct and use a FooT record directly.
Either way, don't forget about simple encapsulation:
data LineDevice -- abstract
-- Some LineDevice constructors for common tasks
stdio :: LineDevice
openFile :: FilePath -> IO LineDevice
connectTo :: HostName -> PortId -> IO LineDevice
getLine :: LineDevice -> Int -> IO ByteString
putLine :: LineDevice -> ByteString -> IO ()
This interface is very easy to understand. If you want to let users make
their own LineDevice objects, you can still provide an "internal" module
with something like this:
data Driver = Driver
{ getLine :: Int -> IO ByteString
, putLine :: ByteString -> IO ()
}
newLineDevice :: Driver -> IO LineDevice
Hope this helps,
-Joey
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20130305/44fddbce/attachment.htm>
More information about the Haskell-Cafe
mailing list