
Hi Wouter, Wouter Swierstra wrote:
Nice! I have to admit, it's much nicer than I expected it to be. Just out of curiousity, what happens when you write:
selectTupleList :: Connection -> IO [Integer]
instead of
selectTupleList :: Connection -> IO [(Integer, String, String)]
What kind of error message do you get? More specifically, is this error caught statically or dynamically.
The type annotation in UseSqlExpr.hs was just for the reader. The compiler can infer the types completely. Thus when I make the suggested change I get a compile time error. It looks like this: UseSqlExpr.hs:27:6: Couldn't match expected type `Integer' against inferred type `(Integer, String, String)' Expected type: IO [Integer] Inferred type: IO [(Integer, String, String)] In the expression: (return $ (map (\ [x0[a2ZY], x1[a2ZZ], x2[a300]] -> (readInteger x0[a2ZY], readString x1[a2ZZ], readString x2[a300])) rows[a2ZX])) In the expression: do rows[a2ZX] <- fetchRows dsn[a2ZW] ['S', 'E', 'L', 'E', 'C', 'T', ' ', 'u', 's', 'e', 'r', '_', 'i', 'd', ',', ' ', 'u', 's', 'e', 'r', '_', 'n', 'a', 'm', 'e', ',', ' ', 'u', 's', 'e', 'r', '_', 'r', 'e', 'a', 'l', '_', 'n', 'a', 'm', 'e', ' ', 'F', 'R', 'O', 'M', ' ', 'u', 's', 'e', 'r', ';'] (return $ (map (\ [x0[a2ZY], x1[a2ZZ], x2[a300]] -> (readInteger x0[a2ZY], readString x1[a2ZZ], readString x2[a300])) rows[a2ZX])) make: *** [all] Fejl 1
The only other limitation I can think of, would be in the situation where you don't have compile-time access to the database, e.g. developing software for a client with a database that can only be accessed from their intranet. I have no idea how much of a limitation that is.
True, but this limitation is only relevant when you do not have access to the production database or a database with identical metadata. How often do people develop like that? How are they testing? I have a hard time picturing a setup without a test database with identical metadata to the production database.
Perhaps I should explain my own thoughts on the subject a bit better. I got interested in this problem because I think it makes a nice example of dependent types "in the real world" - you really want to
But won't you end up implementing all the functionality of an SQL parser? While possible, it does seem like a huge job. With a TH solution you will safe a lot of work.
Yes - but parsing the result of an SQL describe statement is pretty easy. ok.
A library that will be a lot more complex to learn than what I am proposing (assuming the developer already knows SQL).
Hmm. This is a rather sticky point. One might also argue that Haskell developers have to learn SQL to use the solution you propose. I'm not particularly convinced. Both approaches have their merits I think.
Yes. I was _not_ making what you could call a strong argument. I was assuming that most (Haskell) developers knew SQL anyway. I have no data to back it up. Just my gut feeling. To be fair I should mention a couple of drawbacks with the TH-based approach. While SQL got static typing, it is not really as powerful as it could be. For example if you do "select sum(...) from ..." the type system will tell you that a set of values are returned. In reality this set will never have more than one member. Your proposal would be able to return a Float in stead of a [Float]. Another advantage your proposal (and disadvantage of the TH based one) would be that it can abstract over variances in different database implementation. That is, you could translate to SQL depending on SQL backend. This would be really nice. But I guess it would also be a big task.
Anyhow - nice work! Have you asked Bjorn Bringert what he thinks? He's a really clever and approachable guy - and he knows a lot more about interfacing with databases than I do.
Kind regards,
Wouter
/Mads Lindstrøm