-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should tenant status be lifted to the type level? #7
Comments
What does |
A tenant, in the inactive state, because it's a new-tenant and is awaiting email verification. The third term is the reason why the tenant is in an inactive state. |
Ah, thanks. I guess including the definitions would clarify things here. |
Done :) |
I'm curious myself how far one can go with it. The obvious advantage would be that you now can't write code that would call for example
|
Smart constructors? Load the DB row into an intermediate data-type, check the |
But you have one function to load those! What would be its signature? Something like:
(pardon if it's not syntactically correct, never been doing anything like that in Haskell). Then, possible implementation for disableTenant handler:
|
I've recalled I was implementing custom servant authentication, and it is kinda similar. I'm not implying this is the way it should be done (with typeclasses), but that was the answer I got when I asked around. https://gist.github.com/BartAdv/42e62b4d29ffd153c99821fbc47623bd The relevant bit is here: https://gist.github.com/BartAdv/42e62b4d29ffd153c99821fbc47623bd#file-servant-auth-L19 The cookie is decoded and we deserialize the |
Wow! Didn't realise that this would be so hard. How do library functions like |
They don't - they are reading some type for you, and if you employ some higher order stuff then you probably stick some What you're exploring here, dependent typing, is still rather young, so I wouldn't expect any "best practices"-level answers. People are still exploring this, and if you want you should probably just try on your own see if it can solve any problems for you. But since you seem to be exploring "just Haskell" itself, it might be too much to do everything at once. |
Don't think I understand this very well. Btw, stuff like
then, basically there is no easy way to constraint |
Yes - and this is what I meant by
// EDIT To be more specific, when you look at signature of
this means the |
I believe we can use a record to store detailed information about the tenant? rather than solving everything with ADT? I do understand that you want to enforce the types to determine the correctness of the function. right? |
The straightforward and simpler way is to just run with a single |
Basically you want to make sure that a function return a tenant with a specific status. We can move that to tests right? |
A simple way to do this for a fixed, rarely changing definition of TenantStatus is
then
to automatically do this for arbitary, changing definitions of TenantStatus, existential types are needed.
then
|
@wz1000 thanks for taking a stab at this. Seems like something that could work. If I've understood this correctly, you're working with a more general 'SomeTenant Can this be simplified a bit to make it easier to use? Something on the lines of:
|
@wz1000 And thinking about this pragmatically, isn't this simply pushing the "data validation or sanity checks" from
|
cannot work because it desugars to
so
That is exactly what it is doing. However, the distiction between code that requires a By promoting the status to the type level, we would have to perform the sanity check once before using the Tenant, but then never have to bother about it in code again. Each function would simply describe, in its type, the kind of Tenant it expects. Also, the existential quantification dissapears when the Tenant appears in a negative position.
instead of
making the code a bit less clumsy. |
@wz1000 What does "inline existentially quantified" means? |
@BartAdv I mean without an explicit |
@wz1000 and what about:
with
|
@BartAdv Try it. GHC would complain about not supporting impredicative types or something like that. |
@wz1000 right, played a bit in REPL and now I know what's the problem. Also, did a bit of research how it could look in fully dependent language (Idris), and it seems I'd need dependent pairs for it: data TenantStatus = Active | Inactive
data Tenant : TenantStatus -> Type where
MkTenant : String -> (status : TenantStatus) -> Tenant status
tenants : List (Int, (status ** Tenant status))
tenants = [(1, (Active ** MkTenant "John" Active))
,(2, (_ ** MkTenant "Mike" Inactive))] -- can just use underscore to let compiler infer it
loadTenant : Int -> Maybe (status ** Tenant status)
loadTenant tid = lookup tid tenants |
Related thread on Reddit
Is there a distinct advantage of "lifting" the tenant status to the type-level? For example, it can allow us to write functions like:
How would one implement something like this? How does one keep the
tenantStatus
field within the Tenant record, in sync with the "tag" at the type level?The text was updated successfully, but these errors were encountered: