[Haskell-cafe] Newtype, Data.Text and instance declarations...

Brandon Allbery allbery.b at gmail.com
Fri Apr 12 04:59:44 UTC 2019


On Fri, Apr 12, 2019 at 12:42 AM Guy-Laurent Subri <guy-laurent at subri.ch>
wrote:

> I've noticed that if I write
> '''
> instance ToField Table where
>     toField (Table t) = SQLText t
> '''
>
> I have no error and it compiles. I don't get it, what is 'toField'
> supposed to receive except a Table?
>

Haskell does not automatically "convert" types. If something wants a Text,
it will not accept a Table, even though that Table is just a wrapper around
a Text. You must unwrap it to get the Text inside, which is what the (Table
t) pattern is doing: matches a Table by its constructor, and gives you the
contained Text bound to "t".

This is somewhat more obvious when you have a type with multiple fields, or
with multiple constructors, since pattern matching then lets you access all
the fields, and you can have multiple patterns matching different
constructors.

    data Foo = Foo Int Text
    someFoo (Foo i t) = -- do something with the Int i and Text t here

    data Foo = FooI Int | FooT Text
    someFoo (FooI i) = -- do something with the Int i here
    someFoo (FooT t) = -- do something with the Text t here

(You can of course also combine them, multiple constructors each with zero
or more fields.)

2. My second question is concerning the initDB function. If I write it
> that way, the code compiles, but I get an error during execution:
>
> bkmrk: SQLite3 returned ErrorError while attempting to perform prepare
> "CREATE TABLE IF NOT EXISTS :table  (url TEXT, tags TEXT, date
> INTEGER)": near ":table": syntax error
>
> I don't understand what is wrong with the code and I doubt that my query
> is wrong because everything works with the last code snippet I wrote
> (see question #3).
>

In general, you can't use that kind of substitution with SQL Data
Definition Language (e.g. CREATE TABLE), nor is it generally usable to
replace table or column names, only values. You need to concatenate the
actual table name into the command, not a SQL string-like value nor an
embedded SQL placeholder.

3. Trying to get around the error I've noticed a few things. If I try to
> write this instead:
>
> '''
> initDB :: FilePath -> Table -> IO BookmarkDB
> initDB fp table = do
>     let query = Query ("CREATE TABLE IF NOT EXISTS " <> table
>                     <> " (url TEXT, tags TEXT, date INTEGER)")
>     conn <- open fp
>     execute_ conn query
>     return $ BookmarkDB conn table
> '''
>
> it doesn't compile because table is of type Table and not
> Data.Text.Internal.Text, which I get, but I don't know what is the
> Haskell way of resolving this.
>

Same as your first question:

    initDB fp t@(Table table) = do -- which unwraps the Table and gives you
the Text within it as "table".

But you also need the actual Table value, since you use it in the last line
of your function, so you also need the as-pattern.

-- 
brandon s allbery kf8nh
allbery.b at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20190412/dbd1dd39/attachment.html>


More information about the Haskell-Cafe mailing list