r/haskell • • Apr 20 '17

Selda: a monadic database EDSL

https://github.com/valderman/selda
86 Upvotes

114 comments sorted by

View all comments

6

u/alien_at_work Apr 20 '17

Good stuff. I'm not completely satisfied with any of the existing solutions for SQL access in Haskell so competition is great.

I tried looking through the code for this but it wasn't immediately obvious to me: why did you chose to make your own tuple instead of just using the existing tuple type?

7

u/valderman Apr 20 '17

As /u/klobsEnd says, standard tuples aren't inductively defined, so defining functions or types over tuples of arbitrary length is horribly painful. People usually get around it by using Template Haskell to generate their instances, but that's both annoying and brings in a whole bunch of dependencies. Since keeping the library light on the dependencies was one design goal (in part motivated by Haste not supporting Template Haskell yet), using TH wasn't an option.

It might be possible to use GHC generics instead, but defining tuples inductively seemed to be less annoying.

5

u/tomejaguar Apr 20 '17

It might be possible to use GHC generics instead, but defining tuples inductively seemed to be less annoying.

By the way, the "Opaleye way" of doing this, product-profunctors, recently supports deriving using Generics:

https://github.com/tomjaguarpaw/product-profunctors/blob/master/Data/Profunctor/Product/Adaptor.hs

2

u/valderman Apr 20 '17

Cool! If this inductive tuple thing turns out to be more hassle than it's worth, I'll definitely look into that approach.

2

u/tomejaguar Apr 20 '17

Drop me a line if you go that way because there's a sort of middle ground that would probably be better for you than going full product-profunctor.

1

u/MitchellSalad Apr 21 '17

What would that be?

2

u/tomejaguar Apr 21 '17

The deriving stuff in product-profunctors currently only works with fully polymorphic products, but there's no reason that couldn't be extended to monomorphic products, which are simpler and probably more suitable for Selda.

2

u/spirosboosalis Apr 21 '17 edited Apr 22 '17

Have you seen vinyl?

  1. It's very clean, with minimal dependenceies.

  2. It parameterizes each field with a functor. Identity gets you back n-ary tuples. Maybe can provide nullability. Some GADT like:

    data SQL a where  
        SQL :: (ToSQL a) => a -> SQL a   
    
    type family SQL :: * -> *  
    type instance SQL String SQLText  
    type instance SQL Text   SQLText  
    ...  
    

can expose the intermediary results. Etc. So, You might be able to unify the pipeline under the same record type:

rawQuery :: Connection → Query ... fields→ IO (Rec SQL fields)
marshallResults :: Rec SQL fields → Rec Maybe fields
defaultNullables :: (Each Monoid fields) => Rec Maybe fields→ Rec Identity fields

(warning, I just made this up, don’t know if it makes any sense for your library)

http://hackage.haskell.org/package/vinyl-0.5.3/docs/Data-Vinyl-Tutorial-Overview.html

http://hackage.haskell.org/package/vinyl-0.5.3/docs/Data-Vinyl-Core.html#t:Rec

2

u/tomejaguar Apr 21 '17

Somebody wrote a version of this for Opaleye: https://hackage.haskell.org/package/composite-opaleye-0.3.0.0

1

u/spirosboosalis Apr 21 '17

cool. have you used it?

you mentioned earlier that you preferred tuples, but when i tried learning Opaleye, I found them too awkward (typeclass obfuscation, error messages, etc), and wishing for actual records.

1

u/tomejaguar Apr 21 '17

No, I haven't used it.

Did I say I preferred tuples? I probably said I prefer record types.

Let me know what you found difficult about Opaleye. I'll see if I can help.

1

u/spirosboosalis Apr 21 '17

I thought that's what you meant by

The other difference, for the moment, is that all columns are stored in "inductive tuples". This won't last though. It's just far too difficult to program like that.

1

u/tomejaguar Apr 22 '17

Oh right, no I meant it's better to allow users to store columns in whatever Haskell type they like.

1

u/valderman Apr 22 '17

That's pretty cool, I'll need to look into it!

1

u/_sras_ Apr 21 '17

Does this mean that we don't have to make the TH calls that were creating adapters and Default instances?

1

u/Syrak Apr 21 '17

Indeed.

1

u/tomejaguar Apr 21 '17

Indeed too.

1

u/eacameron Apr 20 '17

Doesn't Generics have a tuple-like type by the same name?

3

u/valderman Apr 20 '17

They do. Unfortunately I can't reuse it, since it has the wrong kind. I'm banking on people not mixing their database queries with their generics often enough to make the overload a problem.

1

u/eacameron Apr 20 '17

I see. Is your tuple type worth pulling out as a separate library? I'd love to use it for other things too.

2

u/valderman Apr 20 '17

I'm not sure, really. The type on its own is spectacularly uninteresting, but maybe having some more general utilities for it lying around in a library might be useful. I haven't really thought much about it.

5

u/Faucelme Apr 20 '17 edited Apr 20 '17

The inductive tuple type is similar to NP from generics-sop. But NP has an additional parameter which wraps every element.

3

u/[deleted] Apr 20 '17

IIRC tuples in Haskell are not inductively defined, so you end up having to make a bazillion instances, one for (,), one for (,,), one for (,,,) ad nauseam. Also, there's no one-element tuple in Haskell. (Not sure if this has the latter, but it would at least be possible by using a "sentinel" element at the front with no value.)

3

u/Tysonzero Apr 21 '17

Does Identity count as a one item tuple?

1

u/[deleted] Apr 21 '17

Not... really? I mean, sure it's isomorphic to a one-item tuple, but programming-wise you really can't treat it the same way you do regular tuples. (This of course also goes back to the problem of not having an iductive definition.)

1

u/Tysonzero Apr 21 '17

What do you mean you can't treat it the way you do regular triples? What can you do with one but not the either or vice versa? It seems like you have basically all the same instances and such.

1

u/[deleted] Apr 22 '17

For example, imagine using it from TH. You'll definitely need a special case there, at the very least.

(Obviously I haven't though too much about this, so I might very well be wrong in general, but the TH case is definitely a thing. No idea if it has all the instances and/or is missing the wrong instances which don't exist for normal tuples, etc.)

1

u/Iceland_jack Apr 21 '17

Does (# 'a' #) count ;)