“重载”类型安全功能

时间:2013-08-19 06:48:56

标签: function haskell overloading type-safety

我正在尝试提出一个可以像这样应用的函数定义:

setProperty Left 45 Px
setProperty Position Absolute
setProperty Visible True

我尝试了GADTs

data StyleUnit = Px | Pct | Em
data PositionP = Absolute | Relative | Static

data Property a where
  Position :: Property PositionP
  Left :: Property (Integer -> StyleUnit)
  Visible :: Bool

使用以下签名:

setProperty :: Property a -> a -> IO ()

但不幸的是,这似乎不起作用。这是完全可以实现的,还是我最好只拥有专门的功能版本,比如setPropertyPositionsetPropertyLeft等等?

3 个答案:

答案 0 :(得分:1)

您可以使用TypeFamilies解决此问题。仅当这些语义具有相似的语义时才建议这样做,否则它们的使用可能会使用户感到困惑。我不确定你的类型,所以我在下面的示例实现中对它们进行了一些修改。

{-# LANGUAGE TypeFamilies     #-}
{-# LANGUAGE FlexibleContexts #-}

data StyleUnit = Px | Pct | Em
data PositionP = Absolute | Relative | Static
data Visible = Visible

class Property a where
  type PropertyVal a :: *
  setProperty :: (PropertyVal a) -> a -> IO ()

instance Property StyleUnit where
  type PropertyVal StyleUnit = Either Int Int
  setProperty a _ = print a

instance Property PositionP where
  type PropertyVal PositionP = ()
  setProperty a _ = print a

instance Property Visible where
  type PropertyVal Visible = Bool
  setProperty a _ = print a

答案 1 :(得分:1)

我首先尝试解释问题代码中发生了什么,然后建议将镜头作为替代解决方案。

易于修复的编译错误

首先,您的数据定义会产生错误,因为Visible的类型不会以Property结尾。你想要这个:

data Property a where
  Position :: Property PositionP
  Left :: Property (Integer -> StyleUnit)
  Visible :: Property Bool

Left

的问题

现在您可以写下以下内容:

setProperty :: Property a -> a -> IO ()
setProperty = undefined

但是setProperty Left的类型存在问题,因为您可以在ghci中查看(您可能需要import Prelude hiding (Left)才能使其正常工作)。

:t setProperty Left
setProperty Left :: (Integer -> StyleUnit) -> IO ()

我认为这不是你想要的类型。您可能需要Integer -> StyleUnit -> IO ()

IO ()移至数据类型

实现这一目标的最简单方法是将IO ()移至Property数据类型:

data Property a where
  Position :: Property (PositionP -> IO ())
  Left :: Property (Integer -> StyleUnit -> IO ())
  Visible :: Property (Bool -> IO ())

setProperty :: Property a -> a
setProperty = undefined

这可能适用于setProperty,但目前尚不清楚如何编写getProperty,我猜您也想要这样做。

使用对

也许最好只使用一对价值和单位:

data Property a where
  Position :: Property PositionP
  Left :: Property (Integer, StyleUnit)
  Visible :: Property Bool

setProperty :: Property a -> a -> IO ()
setProperty = undefined

这会给你:

:t setProperty Left
setProperty Left :: (Integer, StyleUnit) -> IO ()

您应该能够实施哪些。

这真的是要走的路吗?

在不知道问题的背景的情况下,我不能说这是否真的是要走的路。对于一个简单的目标来说,这似乎是相当复杂setPositionsetLeftsetVisible出了什么问题?如果你只想将一个setter和一个getter组合成一个抽象,你可能想看一下镜头。

答案 2 :(得分:0)

不确定setProperty到底是什么意思。如果您的意思是创建属性,您可以执行以下操作:

data StyleUnit = Px | Pct | Em
data PositionP = Absolute | Relative | Static

data Property a where
  Position :: PositionP -> Property PositionP
  Left :: (Integer,StyleUnit) -> Property (Integer, StyleUnit)
  Visible :: Bool -> Property Bool

setProperty :: (a -> Property a) -> a -> Property a
setProperty = ($)

示例:

setProperty Left (45,Px)