通常我認為type-families與typeclasses/instances相比具有類似的表現力--區別在于代碼的尷尬/經濟性。在這種情況下,我有代碼在使用type-families來提出一個constraint,但是等同的typeclass代碼不會被編譯。(* Could not deduce (Eq a) ... 當(Eq a)正是我所提供的約束條件時。) 這是一個typeclasses不能表達的情況嗎,還是我做錯了什么?
data Set a = NilSet | ConsSet a (Set a) deriving (Eq, Show, Read)。
-- fmap over a Set, squishing out duplicates[/span]。
fmapSet :: (Eq a, Eq b ) => (a -> b) -> Set a -> Set b
fmapSet f NilSet = NilSet[/span]。
fmapSet f (ConsSet x xs) = uqCons (f x) (fmapSet f xs)
uqCons fx fxs | sElem fx fxs = fxs
|否則 = ConsSet fx fxs
sElem fxNilSet = False
sElem fx (ConsSet fy fys) = fx == fy || sElem fx fys
我想通過一個類似于Functor的類來呼叫那個fmap,其約束條件是資料結構是格式化的。這些方法中的任何一種與type-families都可以作業(基于這個答案,但更傾向于獨立的家族)。
{-# LANGUAGE ConstraintKinds, TypeFamilies #-}
import Data.Kind (Type, Constraint)
type family WFTF (f : :* -> *) a :: Constraint
type實體WFTF Set a = Eq a
class WFTFFunctor f where
wftFfmap :: (WFTF f a, WFTF f b) => (a -> b) -> f a -> f b
instance WFTFFunctor Set where
wftFfmap = fmapSet
type family WFTF2 c_a :: Constraint /span>
type instance WFTF2 (Set a) =Eq a
class WFTF2Functor f where
wftF2fmap :: (WFTF2 (f a), WFTF2 (f b)) => (a -> b) -> f a -> f b
instance WFTF2Functor Set where
wftF2fmap = fmapSet
同等的(我認為)typeclass至少可以編譯,只要我不給方法的實作:
class WFT c_a where>
instance Eq a => WFT (Set a)
class WFTFunctor f where
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance WFTFunctor Set where wftfmap f xss = undefined -- s/b fmapSet f xss
推斷 :t ( f (xss :: Set a) -> wftfmap f xss) :: (Eq a, Eq b) => (a -> b) -> Set a -> Set b -- 這正是fmapSet的型別。但如果我把對fmapSet f xss的呼叫放在undefined的位置,就會被拒絕:
* Could not deduce (Eq a) arising from a use of `fmapSet'
從背景關系:(WFT (Set a), WFT (Set b)
由type簽名來約束:
wftfmap :: forall a b.
(WFT (Set a), WFT (Set b) =>
(a -> b) -> Set a -> Set b
在...。
Possible fix:
將(Eq a)添加到背景關系of。
的type簽名,用于:。
wftfmap :: forall a b.
(WFT (Set a), WFT (Set b) =>
(a -> b) -> Set a -> Set b
WFT (Set a) implies raises Wanted (Eq a), 所以我應該不需要添加它。(如果我通過InstanceSignatures來做,就會被拒絕,因為它不像推斷的約束那樣普遍。) [In response to @dfeuer's answer/my comment] 的確,在實體 decl 中沒有任何東西可以滿足 (Eq a),但是 fmapSet 的簽名也需要 (Eq a),所以(為什么)不能確保約束在呼叫處得到滿足?
我已經嘗試用ScopedTypeVariables/PatternSignatures來裝飾一切,以使約束更加明確。我試著切換到ImpredicativeTypes(GHC 8.10.2)。我有時會得到不同的拒絕資訊,但沒有任何東西可以編譯。
如果我把WFT (Set a)和(Eq a)從fmapSet的簽名中去掉,我得到一個類似的拒絕資訊* Could not deduce (Eq b) ...。是的,我知道這個拒絕資訊是一個FAQ。在我看過的q中,該約束確實是不可滿足的。但是,那么在這種情況下
a) 為什么帶有實作undefined的版本會進行型別檢查;
b) 從WFT (Set a)得到的約束不是被fmapSet滿足的嗎?
fmapSet具有(Eq a)?
補充:要更多地解釋一下我所期望的 Wanted/Satisfied 約束的情況:
uqCons沒有給出簽名,也沒有給出它所呼叫的sElem。在 sElem 中有一個對 (==) 的呼叫,它在 sElem 的簽名中引發了 Wanted (Eq b)。這在uqCons的sig中被傳遞為一個旺旺,這在fmapSet的sig中被傳遞為一個旺旺,這確實有一個包括(Eq b)的sig。
類似地,方法 wftfmap 的 Set 實體引發了 Wanted (Eq a, Eq b);我希望它可以使用它來滿足因呼叫 fmapSet 而產生的 Wanted。
uj5u.com熱心網友回復:
超類約束和實體約束之間有很大的區別。超類約束是形成類的任何實體所需要的東西,并且在子類約束生效的時候是可用的。實體約束是形成一個特定實體所需要的,當類約束生效時,不是自動可用。這種差異已經深入到了系統中,并且反映在了 Core 的表述中。在Core中:
- 類是一種型別。
- 類是類方法的記錄型別和超類. 。
- 一個實體是一個型別別的值或一個從其實體約束到這樣一個值的函式。
一旦一個 "字典函式 "被呼叫以產生一個實體字典,你就只有這個字典了,而沒有用來創建它的引數。
uj5u.com熱心網友回復:
[re @Ben's comment to @dfeuer's answer] not something you have on the inside to implement wftfmap.
好吧,我可以在內部擁有正確的簽名:
-- class/instance WFT, funcs fmapSet, uqCons, sElem as before
{-# LANGUAGE InstanceSigs, QuantifiedConstraints #-}。
class WFTQFunctor f where
wftQfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance (forall b. (WFT (Set b) => Eq b) => WFTQFunctor Set where
wftQfmap :: (Eq a, Eq b) => (a -> b) -> (Set a) -> (Set b) -- yay。
wftQfmap f xss = fmapSet f xss
編譯時有一個警告,我可以用-XMonoLocalBinds來壓制它:
* The constraint `WFT (Set b)' matching
instance Eq a => WFT (Set a)
這使得內部系結的型別推理變得脆弱。
要么使用MonoLocalBinds,要么使用instance來簡化它。
* In the instance declaration for `WFTQFunctor Set'
我很欣賞QuantifiedConstraint是一個纖維。我可能有instance {-# OVERLAPPING #-}。WFT (Set (b -> c)),對于它來說,Eq不成立;但是我沒有;和/或至少如果我使用一個Set元素,對于它來說,Eq成立,我就可以擺脫它(?
但是沒有:
*> wftQfmap toUpper mySet -- mySet :: Set Char
<interactive>:2:1: 錯誤。
* Could not deduce (Eq b) arising from a use of `wftQfmap'
產生的。WFT (Set b)
被一個量化的背景關系約束在<互動>:2:1-22。
Possible修復:將(Eq b)添加到背景關系of一個量化的背景關系中。
* In運算式:wftQfmap toUpper mySet
在"it "的等式中:it = wftQfmap toUpper mySet
那么,為什么那個instance WFTQFunctor Set會被編譯?而且它能做任何有用的事情嗎?
uj5u.com熱心網友回復:
好吧,我有一些作業。它既丑陋又笨重,而且不可擴展,但它回答了這個問題:
class WFT c_a where
isWF :: c_a -> Bool -- is c_a well-formed?
mkWF :: c_a -> c_a --編輯c_a,使其格式化。
mkWF = id
instance Eq a => WFT (Set a) --實體型別與q相同
isWF NilSet = True
isWF (ConsSet x xs) title">xs) = not (sElem x xs) & & isWF xs -- sElem decl'd in the q
mkWF = fmapSet id -- fmapSet也。
class WFTFunctor f where -- class as decl'd in the q
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a -> f b
instance WFTFunctor Set where wftfmap f xss = mkWF $ fmap f xss
在哪里?
instance Functor Set where -- conventional/unconstrained fmap
fmap f NilSet = NilSet
fmap f (ConsSet x xs) = ConsSet (f x) (fmap f xs)
如果我使用fmap,概括地說:
class Functor f => WFTFunctor f where>
wftfmap :: (WFT (f a), WFT (f b)) => (a -> b) -> f a-> f b
wftfmap f xss = mkWF $ fmap f xss
這與H2010標準相差無幾。(需要FlexibleConstraints。)所以我可以讓Eq約束在WFTFunctor實體中生效;它需要從WFT中呼叫一個方法來將其貫穿。
我可以在WFT類中擁有大量的其他方法,但是我說 "不可擴展 "是因為你一般無法將一個不合格的結構 "編輯 "成合格的結構。嗯,因為它是一個Functor:可以卸載到一個List,然后再加載到結構。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/316965.html
標籤:
