我继续尝试使用Haskell和GUI https://github.com/bigos/cairo-example/blob/1c4448a936031b0e5b66b77027be975d040df01b/src/Main.hs 我遇到了另一个问题。
我有两个相同的函数,结果类型不同:
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')
在此功能中使用了一个
updateCanvas :: Gtk.DrawingArea -> Model -> Render ()
,另一个在主函数中使用。
是否可以删除代码重复?
根据Vora的建议,我使用了以下内容:
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)
答案 0 :(得分:5)
最简单的方法是使用Render
为MonadIO
这一事实,这意味着您只需制作一个IO
类型的定义,并在{}}内部调用liftIO
Render
Monad。
编辑:
根据Carl的建议,由于widgetGetAllocatedWidth
/ widgetGetAllocatedHeight
受MonadIO
要求约束,您还可以制作
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')
然后在两个场景中都可以使用。这大致相当,但它允许从任何MonadIO
上下文以相同的方式调用它。
Edit2 :(因为更多编辑总是更好的人)
编辑3:因为有时候你会从太近的地方看一些东西,而你会错过它
为清晰起见,我在liftIO
区域内移动的do
也可以删除,以便对字段进行整理:
getWidgetSize :: MonadIO m => Gtk.DrawingArea -> m (Int, Int)
getWidgetSize widget = do
width' <- fromIntegral <$> Gtk.widgetGetAllocatedWidth widget
height' <- fromIntegral <$> Gtk.widgetGetAllocatedHeight widget
return (width', height')
答案 1 :(得分:3)
是的,我们可以推断出类型:
widgetGetAllocatedWidth :: (HasCallStack, MonadIO m, IsWidget a) => a -> m Int32
widgetGetAllocatedHeight :: (HasCallStack, MonadIO m, IsWidget a) => a -> m Int32
(<$>) :: Functor f => (a -> b) -> f a -> f b
fromIntegral :: (Integral a, Num b) => a -> b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
return :: Monad m => a -> m a
首先我们可以去掉do
- 块:
getWidgetSize widget = do
width' <- fromIntegral <$> widgetGetAllocatedWidth widget
height' <- fromIntegral <$> widgetGetAllocatedHeight widget
return (width', height')
是语法糖:
getWidgetSize widget = fromIntegral <$> widgetGetAllocatedWidth widget >>= \w' -> (
fromIntegral <$> widgetGetAllocatedHeight widget >>= \h' -> (
return (w', h')
)
)
首先我们假设getWidgetSize
的类型为a -> b
,我们假设为widget :: a
(我们稍后会弄清a
,b
等等。
接下来我们看到有一个电话widgetGetAllocatedWidth widget
,这意味着(HasCallStack, MonadIO m, IsWidget a)
成立,而widgetGetAllocatedWidth widget
的回复类型为m Int32
,所以我们现在知道:< / p>
getWidgetSize :: (HasCallStack, MonadIO m, IsWidget a) => a -> b
widget :: (HasCallStack, MonadIO m, IsWidget a) => a
widgetGetAllocatedWidth widget :: (HasCallStack, MonadIO m, IsWidget a) => m Int32
现在我们执行(<$>) fromIntegral (widgetGetAllocatedWidth widget)
,这意味着:
(<$>) :: Functor f => (c -> d) -> f c -> f d
fromIntegral :: (Integral c, Num d) => c -> d
widgetGetAllocatedWidth widget :: (HasCallStack, MonadIO m, IsWidget a) => m Int32
因此,我们得出结论f ~ m
和c ~ Int32
,以及:
(<$>) fromIntegral (widgetGetAllocatedWidth widget) :: (HasCallStack, MonadIO m, IsWidget a, Num d, Functor m) => m d
第二部分fromIntegral <$> widgetGetAllocatedHeight widget
也是如此,因为widgetGetAllocatedHeight
的签名与widgetGetAllocatedWidth
相同,我们得出结论:
(<$>) fromIntegral (widgetGetAllocatedHeight widget) :: (HasCallStack, MonadIO n, IsWidget a, Num e, Functor n) => n e
请注意,此处n
不本身与m
相同(稍后会发现它是),而e
是< em> not 本身与d
相同。
现在我们有三个部分的功能(我们将前两部分缩短为f
和g
,这样我们的函数看起来像:
getWidgetSize widget = f widget >>= (\w -> g widget >>= (\h -> return (w, h)))
使用
f :: (HasCallStack, MonadIO m, IsWidget a, Num e, Functor m) => m d
g :: (HasCallStack, MonadIO n, IsWidget a, Num e, Functor n) => n e
由于(>>=)
函数具有签名(>>=) :: Monad m => m a -> (a -> m b) -> m b
,因此我们知道w
变量的类型为Num d => d
。
我们进一步知道g >>= \h -> return (w, h)
部分的类型为:
g >>= \h -> return (w, h) :: (HasCallStack, MonadIO n, IsWidget a, Num d, Num e, Functor n) => n (d, e)
由于我们将f
与\w -> g >>= \h -> return (w, h)
“绑定”,我们知道m ~ n
。所以我们得出的结论是结果的类型是:
(HasCallStack, MonadIO m, IsWidget a, Num d, Num e, Functor m) => m (d, e)
现在我们可以删除一些重复:因为MonadIO
被定义为:
class Monad m => MonadIO m where -- ...
从那以后:
(...)class Applicative m => Monad m where -- ...
class Functor m => Applicative m where -- ...
我们知道MonadIO m
实际上暗示Functor m
,因此我们可以删除Functor m
,因此实施的最终类型为:
getWidgetSize :: (HasCallStack, MonadIO m, IsWidget a, Num b, Num c) => a -> m (b, c)
getWidgetSize widget = do
width' <- fromIntegral <$> widgetGetAllocatedWidth widget
height' <- fromIntegral <$> widgetGetAllocatedHeight widget
return (width', height')