A type Prism' s a = Prism s s a a( hackage ) 可以被認為是某個結構s與其成員之間的關系a,因此您始終可以從成員 ( a -> s) 中生成結構,但只能選擇性地從結構 ( s -> Maybe a) 中檢索成員。
該模型有助于將 sum 型別與其建構式之一相關聯……以及(在此處更相關)路由編碼和解碼。如果s是編碼后的路由 URL,a是路由型別,那么我們有a -> s表示編碼功能(總是成功)和s -> Maybe a表示解碼功能(可能失敗)。
現在,在這些函式對上,我想添加一個用于編碼和解碼程序的“背景關系”引數(想象一下,解碼程序需要在成功生成相關路由之前“查找”某個資料庫) . 基本上:
encode :: ctx -> a -> s
decode :: ctx -> s -> Maybe a
有沒有一種型別可以模擬這些轉換?它看起來很像 aPrism'但有一個額外的ctx引數。
作為下一步,我想為這個棱鏡定義一個函子,以便它可以轉換所有三種型別:ctx, s, a。我目前有一個這樣的類,但似乎我可能缺少一個可以用來簡化所有這些的現有庫:
class PartialIsoFunctor (f :: Type -> Type -> Type -> Type) where
-- x, y are the context
-- a, b are the structure `s`
-- c, d are the route types `a`
pimap ::
Prism' b a -> -- Note: contravariant prism
Prism' c d ->
(y -> x) -> -- Note: this is contravariant
f x a c ->
f y b d
這里的想法是有一個RouteEncoder ctx r型別(另見)表示一個知道如何編碼/解碼路由的值。我希望能夠將這些路由編碼器轉換為r,和它編碼到/解碼ctx的 URL 字串(實際上是內部的)。FilePath
筆記:
- 我使用
optics-core而不是lens.
編輯:這是我目前的方法:
type RouteEncoder ctx s route = Prism' (ctx, s) (ctx, route)
以及轉換它的函子:
mapRouteEncoder ::
Prism' b a ->
Prism' c d ->
(y -> x) ->
RouteEncoder x a c ->
RouteEncoder y b d
mapRouteEncoder = undefined
編碼/解碼功能:
-- The use of `snd` here suggests that the use of tuples in
-- RouteEncoder is a bit of a hack
encodeRoute :: RouteEncoder ctx r -> ctx -> r -> FilePath
encodeRoute enc ctx r = snd $ review enc (ctx, r)
decodeRoute :: RouteEncoder ctx r -> ctx -> FilePath -> Maybe r
decodeRoute enc m s = snd <$> preview enc (ctx, s)
如何簡化?請注意,RouteEncoder' 是提前創建的,并且是組合的。但實際的編碼/解碼發生在稍后,傳遞隨時間變化的“ctx”。
uj5u.com熱心網友回復:
我和@HTNW 在一起。您應該能夠定義:
type RouteEncoder ctx s route = ctx -> Prism' s route
那么,pimap定義為:
pimap :: Prism' b a -> Prism' c d -> (y -> x)
-> RouteEncoder x a c -> RouteEncoder y b d
pimap p q f r ctx = p . r (f ctx) . q
和定義為encodeRoute:decodeRoute
encodeRoute :: RouteEncoder ctx s r -> ctx -> r -> s
encodeRoute enc ctx r = review (enc ctx) r
decodeRoute :: RouteEncoder ctx s r -> ctx -> s -> Maybe r
decodeRoute enc ctx s = preview (enc ctx) s
唯一的困難是具有延遲背景關系的路由編碼器的組合需要一些額外的語法。你可能需要寫:
\ctx -> otherLens . myRouteEncoder ctx . otherPrism
或類似的,而不是簡單的組合。但是,您現有的解決方案也不能與其他光學器件組合得特別好,除非它們“了解”背景關系。
當您詢問 prism 內部的 getter 函式如何訪問 prism 外部的背景關系時,我不是 100% 確定您的意思。如果您的意思是在定義時,那么答案是您只需使用以下內容:
makeRouteEncoder a b c ctx = prism (f a b ctx) (g c ctx)
myRouteEncoder = makeRouteEncoder myA myB myC
uj5u.com熱心網友回復:
如果您的背景關系是一種適用于程式同一區域中發生的所有事情的“配置”,那么您可能會考慮使用reflection. 您可以使用
type RouteEncoder ctx s r = ctx => Prism' s r
背景關系變得隱含。你會有類似的東西
ctx ~ Zippity x
s ~ Whatever x
只有當你有一個貫穿始終的背景關系時,這才真正符合人體工程學。但是,您可以使用本機類關系在此處添加一些靈活性,從而允許您使用需要更大背景關系“片段”的光學。
例子
假設每個配置方面都表示為一個類:
class FooC x where
getFoo :: Foo
class BarC x where
getBar :: Bar
class (FooC x, BarC x) => ConfigC x where
getBaz :: Baz
現在你會有各種棱鏡看起來大致像
whatever :: forall x. FooC x => Prism' (Thing x) (Thingum x)
yeah :: forall x. BarC x => Prism' (Thingum x) (Yak x)
uhHuh :: forall x. ConfigC x => Prism' (Yak x) Hum
(對不起,不是最好的例子。)
您會注意到,沒有一個棱鏡需要知道它們的配置資訊來自哪里;他們只需要他們需要的東西。
當您撰寫這些時,您將獲得帶有背景關系的棱鏡,這些背景關系累積了必要的背景關系。要實際使用這些棱鏡,您需要一種或多種輔助型別。這里明顯的是
data Config = Config
{ _fooConfig :: Foo
, _barConfig :: Bar
, _bazConfig :: Baz }
data UsingConfig x = UsingConfig
instance Reifies x Config => FooC (UsingConfig x) where
getFoo = _fooConfig $ reflect (Proxy @x)
instance Reifies x Config => BarC (UsingConfig x) where
getBar = _barConfig $ reflect (Proxy @x)
instance Reifies x Config => ConfigC (UsingConfig x) where
getBaz = _bazConfig $ reflect (Proxy @x)
To actually apply the prisms, you'll need to use reify to toss a Config into the air for them to catch.
If some other program or program component is using a different combination of prisms with different needs, it can use its own master configuration type (like Config) and its own tag type (like UsingConfig) to make the necessary context available.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/445383.html
上一篇:創建型:一. 單例模式
