0
votes

This is probably very basic dependently typed programming problem, but I cannot find anything about it. The problem is this: "I have a bunch of messages that I may or may not have to handle depending on some configuration. How do I discriminate between the messages that I need to handle and the messages I don't need to handle at the type level".

So for example, I have some configuration and some messages.

record Configuration where
  constructor MkConfiguration
  handlesOpenClose: Bool
  handlesWillSave: Bool

config : Configuration
config = MkConfiguration { openClose: True, willSave: False }

data Message = FileOpened -- Handle when openClose = True
             | FileClosed -- Handle when openClose = True
             | WillSave   -- Handle when willSave = True

I now want to be able to write something like this:

GetMessagesForConfig : Configuration -> Type
GetMessagesForConfig config = {- 
 config.openClose = true so FileOpened and FileClosed have to be handled,
 config.willSave  = false so WillSave does not have to be handled
-}

MessagesForConfig : Type
MessagesForConfig = GetMessagesForConfig config

handleMessage : MessagesForConfig -> Response
handleMessage FileOpened = {- do something -}
handleMessage FileClosed = {- do something -}
handleMessage WillSave impossible

Is this, or something like this possible?

1

1 Answers

1
votes

One simple way to implement this without doing something like open unions is:

data Message : (openClose : Bool) -> (willSave : Bool) -> Type where
    FileOpened : Message True a
    FileClosed : Message True a
    WillSave   : Message a True
handleMessage : Messages True False -> Response
handleMessage FileOpened = {- do something -}
handleMessage FileClosed = {- do something -}
handleMessage WillSave impossible