In all the examples I have seen the results from esqueleto are projected into a list of tuples or to entities records.
For example:
previousLogItems
What esqueleto is actually doing here is a little complicated. Here is the type for select:
select :: (SqlSelect a r, MonadIO m) => SqlQuery a -> SqlReadT m [r]
This takes an SqlQuery a (a monad wrapping the value you return), and returns an SqlReadT m [r] (a monad wrapping a list of results). When you return your Custom type, the following happens:
a type to persistent's internal SQL representation[r]To make this work for custom types, you'll need to instantiate SqlSelect, and define conversion functions to and from the persistent types:
data Custom' = Custom' (Value Text) (Value Int)
data Custom = Custom
{ title :: Text
, id :: Int
}
instance SqlSelect Custom' Custom where
sqlSelectCols esc (Custom' a b) = (mconcat [ta, ", ", tb], va ++ vb)
where
(ta, va) = sqlSelectCols esc a
(tb, vb) = sqlSelectCols esc b
sqlSelectColCount _ = 2
sqlSelectProcessRow [PersistText a, PersistInt64 b] = Right $ Custom a b
sqlSelectProcessRow _ = Left "Error: Incorrect rows to translate to Custom"
(note that I wasn't actually able to test any of the above, so it might have bugs)
One thing to note is that in the above example, the a and r types are not the same (Custom' vs Custom). This is because inside of a select, all of the values you're working with are of type Value, not their actual types.