Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I remove code duplication in two identical Haskell functions with different output type?

Tags:

haskell

I continue experimenting with Haskell and GUI https://github.com/bigos/cairo-example/blob/1c4448a936031b0e5b66b77027be975d040df01b/src/Main.hs and I've ran into another problem.

I have two identical functions with different result type:

getWidgetSize :: Gtk.DrawingArea -> Render (Int, Int)
getWidgetSize widget = do
  width'  <- fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
  height' <- fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
  return (width', height')

getWidgetSize2 :: Gtk.DrawingArea -> IO (Int, Int)
getWidgetSize2 widget = do
  width'  <- fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
  height' <- fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
  return (width', height')

One is used in this function

updateCanvas :: Gtk.DrawingArea -> Model -> Render ()

and the other is used in the main function.

Is it possible to remove the code duplication?

solution

Following Vora's advice I have used the following:

getWidgetSize :: Gtk.DrawingArea -> IO (Int, Int)
getWidgetSize widget = do
  width'  <- fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
  height' <- fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
  return (width', height')

updateCanvas :: Gtk.DrawingArea -> Model -> Render ()
updateCanvas canvas model = do
  size <- liftIO (getWidgetSize canvas)
like image 913
ruby_object Avatar asked Jan 28 '23 03:01

ruby_object


1 Answers

Easiest way would be to use the fact that Render is a MonadIO, meaning you could simply make one IO typed definition, and liftIO the call when inside the Render Monad.

Edit: As per Carl's suggestion, and since widgetGetAllocatedWidth/widgetGetAllocatedHeight are constrained on the MonadIO requisite, you could also make a

getWidgetSize :: MonadIO m => Gtk.DrawingArea -> m (Int, Int)
getWidgetSize widget = do
  width'  <- liftIO $ fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
  height' <- liftIO $ fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
  return (width', height')

which would then work in both scenario. This is mostly equivalent, however it allows it to be called the same way from any MonadIO context.

Edit2: (Because more edits is always better right guys)
Edit3: Because sometimes you look at something from too close, and you miss it.
The liftIO, which I moved inside the do block for clarity, can also be removed to declutter the field a bit :

getWidgetSize :: MonadIO m => Gtk.DrawingArea -> m (Int, Int)
getWidgetSize widget = do
  width'  <- fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
  height' <- fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
  return (width', height')
like image 86
Vora Avatar answered May 12 '23 17:05

Vora