Selda, type operators and heterogeneous lists

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Selda, type operators and heterogeneous lists

Marc Busqué
Hi there!

I'm using [selda](https://github.com/valderman/selda) package to deal
with relational databases.

This package represents database tables in haskell in a type-safe way.
And to do so it leans on type operators. For example, here they are two
different tables:

```
categories :: Table (RowID:*:Text)
expenses :: Table (RowID:*:Text:*:Double:*:RowID)
```

As both of them belong to different types, I can't put them in a single
list out of the box:

```
[categories, expenses] --- not valid!!
```

I'm aware of [different solutions](https://wiki.haskell.org/Heterogenous_collections) to deal with it, but they don't convince me:

- Algebraic data types: I think in this situation it is specially a
   burden having to play type-switching... There can exist a lot of
   different table schemas and they may change at any moment.

- Universal type: I see it as a kind of hack

- Existential types: I have read that usually it is an anti-pattern, and
   in this case I don't understand how it should work, because types are
   constructed with `:*:` operator.

So my question if it exists some natural way to treat types built with
type operators as a single type. I have to recognize I don't understand
well how all this stuff about type operators works. I have read
something but I still don't get it. So any direction with it would be
very gratifying, too :)

Thanks in advance.

Marc Busqué
http://waiting-for-dev.github.io/about/
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Tom Ellis
On Fri, Apr 13, 2018 at 03:59:44PM +0200, Marc Busqué wrote:
> ```
> categories :: Table (RowID:*:Text)
> expenses :: Table (RowID:*:Text:*:Double:*:RowID)
> ```
>
> As both of them belong to different types, I can't put them in a single
> list out of the box:

Before we can help we need to know more.  Specifically, why do you want to
put them in a single list?
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Marc Busqué
On Fri, 13 Apr 2018, Tom Ellis wrote:

> On Fri, Apr 13, 2018 at 03:59:44PM +0200, Marc Busqué wrote:
>
> Before we can help we need to know more.  Specifically, why do you want to
> put them in a single list?

I want to make the same action with more than one (creating them in
the database server):

```
migrate :: IO ()
migrate = do
     dir <- dBDir
     createDirectoryIfMissing True dir
     forM_ [categories, expenses]
        $ withDB . createTable
```

Marc Busqué
http://waiting-for-dev.github.io/about/
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Ben Franksen
Am 13.04.2018 um 16:38 schrieb Marc Busqué:

> On Fri, 13 Apr 2018, Tom Ellis wrote:
>
>> On Fri, Apr 13, 2018 at 03:59:44PM +0200, Marc Busqué wrote:
>>
>> Before we can help we need to know more.  Specifically, why do you
>> want to
>> put them in a single list?
>
> I want to make the same action with more than one (creating them in
> the database server):
>
> ```
> migrate :: IO ()
> migrate = do
>     dir <- dBDir
>     createDirectoryIfMissing True dir
>     forM_ [categories, expenses]
>        $ withDB . createTable ```

So a possible solution is to store a list of actions instead of a list
of items (untested code):

    let dbCreateTable = withDB . createTable
    sequence_ $
        map dbCreateTable categories ++ map dbCreateTable expenses

or perhaps, if the types allow it:

    withDB $ do
        let dbCreateTable = withDB . createTable
        sequence_ $
            map createTable categories ++ map createTable expenses

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Tom Ellis
In reply to this post by Marc Busqué
On Fri, Apr 13, 2018 at 04:38:46PM +0200, Marc Busqué wrote:

> On Fri, 13 Apr 2018, Tom Ellis wrote:
> >On Fri, Apr 13, 2018 at 03:59:44PM +0200, Marc Busqué wrote:
> >
> >Before we can help we need to know more.  Specifically, why do you want to
> >put them in a single list?
>
> I want to make the same action with more than one (creating them in
> the database server):
>
> ```
> migrate :: IO ()
> migrate = do
>     dir <- dBDir
>     createDirectoryIfMissing True dir
>     forM_ [categories, expenses]
>        $ withDB . createTable ```

I think I would just tolerate

      forM_ [createTable categories, createTable expenses]
        $ withDB
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Marc Busqué
Thanks for both answers. It wasn't what I had in mind, but surely it is just that I have to get used to Haskell strong typing. Until now I think I'm used to apply DRY beyond types :)

So, from you answers, I can conclude that there is no way to tell something like the following in a type signature: "any type build with that type operators". Isn't it?

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Jake
I would argue that in this case existential types actually are the correct tool. What you want to do is hide some amount of type information, which is exactly what existential types do. Then, because createTable can handle any Table a when you unwrap the Table from the existential type you can still pass it to createTable.

Here's a sort of mock example:

```{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE LambdaCase #-}

import Control.Monad (forM_)

data Table a

data a :*: b where
  (:*:) :: a -> b -> a :*: b
infixr 1 :*:

type RowID = Int
type Text = String

categories :: Table (RowID :*: Text)
categories = undefined

expenses :: Table (RowID:*:Text:*:Double:*:RowID)
expenses = undefined

createTable :: Table a -> IO ()
createTable _ = return ()

data ExTable = forall a. ExTable (Table a)

main :: IO ()
main = forM_ [ExTable categories, ExTable expenses] (\case ExTable t -> createTable t)
```

In your example this requires more boilerplate and doesn't seem much better than [createTable categories, createTable expenses], but this provides a way to actually have a list of tables of differing types without applying createTable to them first and I think that's closer to what you were going for.

On Sun, Apr 15, 2018 at 12:35 PM Marc Busqué <[hidden email]> wrote:
Thanks for both answers. It wasn't what I had in mind, but surely it is just that I have to get used to Haskell strong typing. Until now I think I'm used to apply DRY beyond types :)

So, from you answers, I can conclude that there is no way to tell something like the following in a type signature: "any type build with that type operators". Isn't it?
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Selda, type operators and heterogeneous lists

Marc Busqué
> I would argue that in this case existential types actually are the correct tool. What you want to do is hide some amount of type information,
> which is exactly what existential types do. Then, because createTable can handle any Table a when you unwrap the Table from the existential type
> you can still pass it to createTable.

Thanks Jake for your response. I think it makes a lot of sense. I'll
give it a try.

Marc Busqué
http://waiting-for-dev.github.io/about/
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.