本傑明·霍奇森的解決方案更簡單,更優雅,但這裏有一個類型級編程解決方案,使用singletons
包的機器。
這個想法是策略表示爲類型級別列表(Role, Operation)
元組,其中Role
和Operation
必須在整個列表中不下降。這樣,我們不能有一個荒唐的[(Public,Edit),(Owner,View)]
權限。
一些必要的擴展和進口:
{-# language PolyKinds #-}
{-# language DataKinds #-}
{-# language TypeFamilies #-}
{-# language GADTs #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# language FlexibleInstances #-}
{-# language ScopedTypeVariables #-}
{-# language TemplateHaskell #-}
import Data.Singletons
import Data.Singletons.TH
import Data.Promotion.Prelude (Unzip)
我們宣佈的數據類型和使用singletonize這些模板哈斯克爾:
data Role = Public | Contributor | Owner deriving (Show,Eq,Ord)
data Operation = None | View | Edit deriving (Show,Eq,Ord)
$(genSingletons [''Role,''Operation])
$(promoteEqInstances [''Role,''Operation])
$(promoteOrdInstances [''Role,''Operation])
一種列表類與非減元素:
class Monotone (xs :: [k])
instance Monotone '[]
instance Monotone (x ': '[])
instance ((x :<= y) ~ True, Monotone (y ': xs)) => Monotone (x ': y ': xs)
給定一個指定爲類型級別列表的策略,返回策略函數:
policy :: forall (xs :: [(Role, Operation)]) rs os.
(Unzip xs ~ '(rs,os), Monotone rs, Monotone os)
=> Sing xs
-> Role
-> Operation
policy singleton role =
let decreasing = reverse (fromSing singleton)
allowed = dropWhile (\(role',_) -> role' > role) decreasing
in case allowed of
[] -> None
(_,perm) : _ -> perm
測試在ghci中試驗:
ghci> :set -XDataKinds -XPolyKinds -XTypeApplications
ghci> policy (sing::Sing '[ '(Public,View),'(Owner,Edit) ]) Owner
Edit
ghci> policy (sing::Sing '[ '(Public,Edit),'(Owner,View) ]) Owner
*unhelpful type error*
我是正確的,GHC能夠推導一個Monoid實例'Policy'因爲'最大了'是一個Monoid和'X - >含半幺羣y'是一個Monoid。我也可以派生自己的'實例:''(Policy a)'mappend'(Policy b)= Policy $ \ r - > max(ar)(br)'' – homam
是的,雖然GHC會產生完全相同的代碼,那麼爲什麼要寫它呢? –
這是一個非常優雅的解決方案。謝謝! – homam