Codifying presence/absence of authentication at type level

人盡茶涼 提交于 2019-12-01 03:35:41

Phantom types to the rescue! is Bryan O'Sullivan example of implementing read-only vs read/write access at the type level using phantom types.

Similarly, for your use-case:

data Unknown       -- unknown users
data Authenticated -- verified users

newtype User a i = Id i deriving Show

It is important that the data constructor Id is not exposed to user, but the module provides functions to initialize and authenticate users:

-- initializes an un-authenticated user
newUser :: i -> User Unknown i
newUser = Id

-- authenticates a user
authUser :: (User a i) -> User Authenticated i
authUser (Id i) = Id i  -- dummy implementation

then, you may control access at the type level without code duplication, without run-time checks and without run-time cost:

-- open to all users
getId :: User a i -> i
getId (Id i) = i

-- only authenticated users can pass through
getId' :: User Authenticated i -> i
getId' (Id i) = i

For example, if

\> let jim = newUser "jim"
\> let joe = authUser $ newUser "joe"

joe is an authenticated user and can be passed to either function:

\> getId joe
"joe"
\> getId' joe
"joe"

whereas, you will get compile-time error if you call getId' with jim:

\> getId jim
"jim"
\> getId' jim   -- compile-time error! not run-time error!

<interactive>:28:8:
    Couldn't match type ‘Unknown’ with ‘Authenticated’
    Expected type: User Authenticated [Char]
      Actual type: User Unknown [Char]
    In the first argument of ‘getId'’, namely ‘jim’
    In the expression: getId' jim
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!