Skip to content
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

Define coverings outside of surjective's scope #8

Open
gelisam opened this issue Feb 24, 2018 · 1 comment
Open

Define coverings outside of surjective's scope #8

gelisam opened this issue Feb 24, 2018 · 1 comment
Labels
enhancement New feature or request

Comments

@gelisam
Copy link
Owner

gelisam commented Feb 24, 2018

The current API is very noisy:

listMaybeBools :: [Maybe Bool]
listMaybeBools = $$(surjective
  [||...        [ covers $ \(Just True)  -> Just True
                , covers $ \(Just False) -> Just False
                ]
  ||])

@rpglover64 suggested a nicer, less noisy API:

t = covers $ \(Just True)  -> Just True
f = covers $ \(Just False) -> Just False

listMaybeBools :: [Maybe Bool]
listMaybeBools = $$(surjective
  [||...        [ t
                , f
                ]
  ||])

Thanks to referential transparency, such a substitution is valid in any Haskell program... except this one, because surjective is a macro and it looks at the syntax of the code it checks, not the semantics. Unfortunately, t is only semantically equal to covers $ \(Just True) -> Just True, it is not syntactically identical.

So, would it be possible to support this syntax? I think it would! Macros can lookup identifiers, so surjective could lookup each identifier in the code it is checking, and if the definition of the identifier is a literal call to covers, surjective could act as if it has seen that call directly.

The obvious problem with this approach is that it would only support one level of indirection. If we were to write this instead:

t' = covers $ \(Just True)  -> Just True
f' = covers $ \(Just False) -> Just False

t = t'
f = f'

listMaybeBools :: [Maybe Bool]
listMaybeBools = $$(surjective
  [||...        [ t
                , f
                ]
  ||])

Then it wouldn't work, for the same reason as before: surjective would lookup t, and while its definition, t', is semantically equal to covers $ \(Just True) -> Just True, it isn't syntactically identical. We could add yet another special case, but then what about (t, f) = (t', f')? Or t = head [t', f']?
Eventually we have to draw the line, otherwise we'll end up writing a Haskell interpreter. I think "a literal call to covers" is the right stopping point, because it allows the definition of t to be moved outside of surjective's scope, making the syntax lighter. Then if we really need to compute t from some more complex expression, we can always use TH to produce a literal call to covers.

@gelisam gelisam added the enhancement New feature or request label Feb 24, 2018
@rpglover64
Copy link
Collaborator

There is an alternative (though I'm not sure how much I like it). Sometimes the solution is to return to the object level: covers is a TH function which takes a lambda :: a -> aand produces a ([Pattern], a), or a Writer [Pattern] a, or some newtype thereof; surjective accumulates those values and produces the pattern match warning.

Another idea: an indicator for explicitly propagating covers (I'm thinking a bit analogous to Python's yield from).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants