informatikr / hedis

A Redis client library for Haskell.
http://hackage.haskell.org/package/hedis
BSD 3-Clause "New" or "Revised" License
329 stars 127 forks source link

Database.Redis.PortNumber won't accept a non-literal Int #169

Closed JivanRoquet closed 3 years ago

JivanRoquet commented 3 years ago

With the following configuration record:

data RedisConfig = RedisConfig {
    redisHost :: Text,
    redisPort :: Int,
    redisDB   :: Integer
}

I want to create a connection as follows:

fetchRedisConnection :: RedisConfig -> ConnectInfo
fetchRedisConnection config =
    defaultConnectInfo {
        connectHost = T.unpack $ redisHost config,
        connectPort = PortNumber $ redisPort config,
        connectDatabase = redisDB config
    }

But I couldn't find any way to make PortNumber accept anything else than a literal Int. For instance, PortNumber 6379 works but even if redisPort config == 6379, doing PortNumber $ redisPort config just won't make it. I tried explicitly typing as a PortID or as an Int, nothing works.

This appears inconsistent with the connectHost field, which is perfectly happy with my non-literal String even though it expects a HostName.

The compiler error that I get with the above code is as follows:

• Couldn't match expected type ‘network-3.1.1.1:Network.Socket.Types.PortNumber’
              with actual type ‘Int’
• In the second argument of ‘($)’, namely ‘redisPort config’
  In the ‘connectPort’ field of a record
JivanRoquet commented 3 years ago

After a bit of research, looks like there are two sides of this problem. First, there is a Database.Redis.PortNumber constructor, which will not take a non-literal Int. Second, there is a Network.Socket.PortNumber type which will not accept a non-literal Int as well.

So, neither of these will work:

import Database.Redis as Redis
import Network.Socket as NS

myValue = 6379

myValue :: NS.PortNumber -- nope
Redis.PortNumber myValue -- nope
Redis.PortNumber $ myValue :: NS.PortNumber -- not a chance
Redis.PortNumber $ myValue :: Redis.PortID -- try again

I'm absolutely certain that there must be a way to do this (I guess I'm not the only one trying to connect from dynamically loaded credentials instead of compile-time literals), but I haven't found any so far, and the documentation doesn't seem to say anything about this.

k-bx commented 3 years ago

Yeah, it's somewhat weird, but the solution is to use a fromIntegral method.

fetchRedisConnection :: RedisConfig -> ConnectInfo
fetchRedisConnection config =
  defaultConnectInfo
    { connectHost = T.unpack $ redisHost config,
      connectPort = PortNumber $ fromIntegral (redisPort config),
      connectDatabase = redisDB config
    }
JivanRoquet commented 3 years ago

Very neat, thanks a lot.