2017-09-26 65 views
0

TL展开;博士:是否有可能使用任何lens家族的抽象的包装/解开任意newtype(即提供了一种用于这样的抽象的实例)?如何消除包装的样板和使用镜片

我会通过一个基于真实故事的简单示例来激发我的问题。假设我定义以下newtype

newtype FreeMonoid a = FreeMonoid { asMap :: Map a Int } 

它是用来表示形式方面:

a0 <> a1 <> ... <> an-1 

我们可以代表自由类群如列表:

instance Ord a => IsList (FreeMonoid a) where 
    type Item (FreeMonoid a) = a 
    fromList xs = FreeMonoid $ Map.fromListWith (+) $ zip xs (repeat 1) 
    toList (FreeMonoid p) = do 
     (x, n) <- Map.toList p 
     genericReplicate n x 

两个例子游离单糖是产物序列和序列:

type FreeSum a = FreeMonoid (Sum a) 
type FreeProduct a = FreeMonoid (Product a) 

其中SumProductData.Monoid中定义。现在,我们可以定义fromListtoList操作为FreeSumFreeProduct如下:

fromListSum :: Ord a => [a] -> FreeSum a 
fromListSum = fromList . (Sum <$>) 

fromListProduct :: Ord a => [a] -> FreeProduct a 
fromListProduct = fromList . (Product <$>) 

但是,这里有相当多的样板。这将会是更好,如果我们可以简单地说:

fromListW :: (Ord a, Wrapper f) => [a] -> FreeMonoid (f a) 
fromListW = fromList . (wrap <$>) 

其中wrap是(hypotetical)的一些操作Wrapper类分别为:

wrap :: a -> f a 
unwrap :: f a -> a 

同样,我希望能够写的函数:

toListW :: (Ord a, Wrapper f) => FreeMonoid (f a) -> [a] 
toListW = (unwrap <$>) . toList 

镜头似乎在Control.Lens.Wrapped提供这样的抽象(对于该SumProduct在这个例子中是斯塔那里的特殊类的东西!)。然而,我在这个模块中理解和使用抽象的尝试失败了。例如:

fromListW :: (Ord a, Wrapped (f a)) => [a] -> FreeMonoid (f a) 
fromListW = fromList . (Wrapped <$>) 

将不起作用,因为参数不是Unwrapped (f a)的列表。

所以我的问题是:

  • 待办事项镜头提供与此类似Wrapper类的抽象?
  • 如果不是,可以通过使用镜头解决这个“scrap-your-boilerplate”问题吗?

回答

1

“问题”是,你正在使用Wrapped,这是真正的意思是方便格局代名词,而不是包装“构造”。因为它的目的是支持多态的包裹,你需要断言你的类型可以被重新包装:

fromListW :: (Rewrapped a a, Ord a) => [Unwrapped a] -> FreeMonoid a 
fromListW = fromList . (Wrapped <$>) 

这就按预期工作:

> let x = [1,2,3] 
> fromListW x :: FreeMonoid (Sum Int) 
FreeMonoid {asMap = fromList [(Sum {getSum = 1},... 
> fromListW x :: FreeMonoid (Product Int) 
FreeMonoid {asMap = fromList [(Product {getProduct = 1},... 
> 

我觉得更地道的镜头实现将是:

fromListW :: (Rewrapped a a, Ord a) => [Unwrapped a] -> FreeMonoid a 
fromListW = fromList . view (mapping _Unwrapped) 

这仍然需要Rewrapped a a约束,但可以使用非多态性_Unwrapped'代替:

fromListW :: (Wrapped a, Ord a) => [Unwrapped a] -> FreeMonoid a 
fromListW = fromList . view (mapping _Unwrapped') 

看起来更自然一点。

toListW实施将有类似的结构:

toListW :: (Wrapped a, Ord a) => FreeMonoid a -> [Unwrapped a] 
toListW = view (mapping _Wrapped') . toList 
+0

感谢伟大的答案。目前还不清楚该解决方案的工作原理和原因,但我希望在我深入研究镜头之后能够了解这一点。在此期间[这里](https://github.com/capitanbatata/sandbox/blob/master/pw-lenses/src/corercions.lhs)是一些工作代码。 –