haskell - Codifying presence/absence of authentication at type level -


context: i'm approaching haskell standpoint of converting runtime errors compile-time errors. hypothesis possible if 1 can codify business logic within program's types itself.

i'm writing telegram bot, should accessible users within company. achieve "restriction", whenever starts chatting bot chat_id in table , check if valid oauth_token exists. if not, user first sent link complete google oauth (our company's email hosted on google apps business).

share [mkpersist sqlsettings, mkmigrate "migrateall"] [persistlowercase| vluser   email string   chatid integer   tgramuserid integer   tgramfirstname string   tgramlastname string maybe   tgramusername string maybe   oauthtoken string maybe   deriving show |] 

users valid oauth_token able give telegram bot commands, unauthenticated users should not able give.

now, i'm trying codify logic @ type level itself. there functions in haskell code have ability accept, arguments, both, authenticated & unauthenticated users; while functions should accept authenticated users.

if keep passing user objects of same type, i.e. vluser everywhere, have careful check presence of oauthtoken in every function. there way create 2 user types - vluser , vluserauthenticated where:

  1. both map same underlying table
  2. a vluserauthenticated can instantiated if has oauthtoken

phantom types rescue! bryan o'sullivan example of implementing read-only vs read/write access at type level using phantom types.

similarly, use-case:

data unknown       -- unknown users data authenticated -- verified users  newtype user = id deriving show 

it important data constructor id not exposed user, module provides functions initialize , authenticate users:

-- initializes un-authenticated user newuser :: -> user unknown newuser = id  -- authenticates user authuser :: (user i) -> user authenticated authuser (id i) = id  -- dummy implementation 

then, may control access @ type level without code duplication, without run-time checks , without run-time cost:

-- open users getid :: user -> getid (id i) =  -- authenticated users can pass through getid' :: user authenticated -> getid' (id i) = 

for example, if

\> let jim = newuser "jim" \> let joe = authuser $ newuser "joe" 

joe authenticated user , can passed either function:

\> getid joe "joe" \> getid' joe "joe" 

whereas, compile-time error if call getid' jim:

\> getid jim "jim" \> getid' jim   -- compile-time error! not run-time error!  <interactive>:28:8:     couldn't match type ‘unknown’ ‘authenticated’     expected type: user authenticated [char]       actual type: user unknown [char]     in first argument of ‘getid'’, namely ‘jim’     in expression: getid' jim 

Comments

Popular posts from this blog

routing - AngularJS State management ->load multiple states in one page -

python - GRASS parser() error -

Swift game error message -