Esqueleto to return just a single column

283 Views Asked by At

I'm quite new to Haskell, currently doing my best to prevent my brain from going inside out. I've got a very simple DB schema defined in that way:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
  Instrument
    ticker String
    source String
    exchange String
    deriving Eq Show
|]

I noticed that I can query for the all entries in the table in the following way:

getAllInstruments :: (MonadIO m, MonadLogger m) => SqlReadT m [Entity Instrument]
getAllInstruments = select $ from $ \instrument -> do return instrument

However, what if I'd like to get just a single column ? I tried something like this:

-- doesn't compile
getAllTickers :: (MonadIO m, MonadLogger m) => SqlReadT m [String]
getAllTickers = select $ from $ \instrument -> do return (Value (instrument ^. ticker))

But I get an error message:

• Couldn't match expected type ‘EntityField val0 typ0’
  with actual type ‘StockPrice -> Ticker’

I found a similar question on how to return a subset of columns here: Haskell Esqueleto project subset of columns to list of custom records

And my question is: do I need to implement all the types like described in the article I posted in my application as well (i.e. only for a single column/field) ?

EDIT

Thanks to the hint I managed to come up with the following piece of code:

getAllTickers' :: (MonadIO m, MonadLogger m) => SqlReadT m [String]
getAllTickers' = do 
  res <- select $ from $ \i -> return (i ^. InstrumentTicker)
  return $ map unValue res

1.) Is it acceptable solution ? 2.) For my education: is there a way to avoid the "<-" notation ? Specifically I wonder if there's a way to write it in the following way:

-- Doesn't compile - need to enter with the map into the m***d - how ?
getAllTickers'' = map unValue (select $ from $ \i -> return (i ^. InstrumentTicker))
2

There are 2 best solutions below

0
On

For your last question, try:

getAllTickers'' = map unValue <$> (select $ from $ \i -> return (i ^. InstrumentTicker))
1
On

I do not have any experience with esqueleto, but after a quick look at the tutorials I think this should work:

getAllTickers :: (MonadIO m, MonadLogger m) => SqlReadT m [Value String]
getAllTickers = select $ from $ \instrument -> return (instrument ^. InstrumentTicker)

note different type in signature and different lens. Hope it helps.

EDIT:

I don't see anything wrong with unValue in your solution, but don't cite me on that. If you want to avoid do-notation, one of the ways is you can exploit the fact that a Monad is also a Functor, so you can use infix <$> also known as fmap :

getAllTickers' :: (MonadIO m, MonadLogger m) => SqlReadT m [String]
getAllTickers' = 
  map unValue <$> (select $ from $ \i -> return (i ^. InstrumentTicker))