2017-09-26 39 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)是一些工作代碼。 –