Codifying presence/absence of authentication at type level

后端 未结 1 748
醉梦人生
醉梦人生 2021-01-12 01:42

Context: I\'m approaching Haskell from a standpoint of converting runtime errors to compile-time errors. My hypothesis is that this is possible if one can c

相关标签:
1条回答
  • 2021-01-12 02:01

    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
    
    0 讨论(0)
提交回复
热议问题