Reputation: 6793
I'm trying to "shadow" the Scotty module using the PackageImports
extension.
Use case: For someone using a library X
make instrumentedX
available. So, simply by changing X
to instrumentedX
in their cabal file, they can use the instrumented version of the library -- without any other change in code.
Here's the module that I want to export, which has the same name, i.e. Web.Scotty.Trans
as the original module exported by Scotty.
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PackageImports #-}
module Web.Scotty.Trans
(
module OS
, get
, post
, put
, delete
, patch
, options
, addroute
)
where
import qualified "scotty" Web.Scotty.Trans as OS hiding (get, post, put, delete, patch, options, addroute)
import qualified "scotty" Web.Scotty.Trans as S
import InstrumentedCore
import Control.Monad.Trans.Class(lift)
instrumentedAction original action = original action_
where
action_ = do
st <- liftIO $ getCurrentTime
result <- action
en <- liftIO $ getCurrentTime
logInstrumentationData InstrumentationData{instrStart=st, instrEnd=en, instrPayload=Render}
return result
get route action = instrumentedAction (S.get route) action
post route action = instrumentedAction (S.post route) action
put route action = instrumentedAction (S.put route) action
delete route action = instrumentedAction (S.delete route) action
patch route action = instrumentedAction (S.patch route) action
options route action = instrumentedAction (S.options route) action
addroute method route action = instrumentedAction (S.addroute method route) action
Here's what my cabal file looks like. While my package depends on scotty
it uses a different package name for itself, i.e. instrumentedopaleye
(don't get into the semantics -- I'm trying to shadow a lot of other modules as well!)
name: instrumentedopaleye
version: 0.1.0.0
synopsis: Initial project template from stack
description: Please see README.md
homepage: https://github.com/githubuser/dashboard#readme
license: BSD3
author: Author name here
maintainer: [email protected]
copyright: 2016 Author name here
category: Web
build-type: Simple
extra-source-files: README.md
cabal-version: >=1.10
library
hs-source-dirs: src
exposed-modules: InstrumentedOpaleye
, Instrumented
, InstrumentedOpaleye.Internal.PGTypes
, InstrumentedOpaleye.Internal.Column
, InstrumentedOpaleye.Internal.RunQuery
, InstrumentedOpaleye.Internal.HaskellDB.PrimQuery
, InstrumentedLucid
, Web.Scotty.Trans
, InstrumentedCore
build-depends: base >= 4.7 && < 5
, opaleye
, profunctors
, product-profunctors
, postgresql-simple
, mtl
, time
, stm
, text
, uuid
, lucid
, scotty
, transformers
default-language: Haskell2010
The actual error
And here's what happens when I try to import my version of Web.Scotty.Trans
in the GHCi. Given that I have explicitly imported only my version of Web.Scotty.Trans
why is GHCi getting confused in resolving the get
function? Even :browse Web.Scotty.Trans
is getting confused.
Saurabhs-MacBook-Pro:instrumentedopaleye saurabhnanda$ stack exec ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> :set -fobject-code
Prelude> :set -XPackageImports
Prelude> import "instrumentedopaleye" Web.Scotty.Trans
Prelude Web.Scotty.Trans> :i get
<interactive>:1:1: error:
Ambiguous interface for ‘Web.Scotty.Trans’:
it was found in multiple packages:
scotty-0.11.0 instrumentedopaleye-0.1.0.0
Prelude Web.Scotty.Trans> :browse Web.Scotty.Trans
<no location info>: error:
Ambiguous module name ‘Web.Scotty.Trans’:
it was found in multiple packages:
[email protected] instrumentedopaleye-0.1.0.0@instrumentedopaleye-0.1.0.0-EAOrgrhUGi83yaJJ8gDGX4
Upvotes: 2
Views: 431
Reputation: 6793
I was unable to make this work in GHCi without explicitly hiding scotty
as per [crockeea's answer][1]
Also, due to [Haskell's module limitation][2] one cannot re-export a qualified import. Therefore, here's how I managed to solve my original problem of shadowing scotty:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PackageImports #-}
module Web.Scotty.Trans
(
module Web.Scotty.Trans -- which is the current module
, module Web.Scotty.TransOriginal -- the original module MINUS the shadowed methods
)
where
import Web.Scotty.TransOriginal
import qualified "scotty" Web.Scotty.Trans as S
import InstrumentedCore
import Control.Monad.Trans.Class(lift)
instrumentedAction original action = original action_
where
action_ = do
st <- liftIO $ getCurrentTime
result <- action
en <- liftIO $ getCurrentTime
logInstrumentationData InstrumentationData{instrStart=st, instrEnd=en, instrPayload=Render}
return result
get route action = instrumentedAction (S.get route) action
post route action = instrumentedAction (S.post route) action
put route action = instrumentedAction (S.put route) action
delete route action = instrumentedAction (S.delete route) action
patch route action = instrumentedAction (S.patch route) action
options route action = instrumentedAction (S.options route) action
addroute method route action = instrumentedAction (S.addroute method route) action
The boilerplate Web.Scotty.TransOriginal
which is required only to get around Haskell's limitation around re-exporting qualified imports:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PackageImports #-}
module Web.Scotty.TransOriginal
(
module Web.Scotty.Trans
)
where
import "scotty" Web.Scotty.Trans hiding (get, post, put, delete, patch, options, addroute)
Upvotes: 1
Reputation: 21811
I'm seeing this behavior even before you do a package-qualified import. For example, if I have both cryptonite
and crypto-api
installed with stack previously, then
> stack exec ghci
$ :browse Crypto.Random
causes an ambiguous module name
error. This is a problem I've run into a lot; my solution is to use the -hide-package
flag:
> stack exec ghci
$ :set -hide-package cryptonite
$ :browse Crypto.Random
As an added bonus, you no longer need PackageImports
at all: ghci is completely ignoring cryptonite
.
Upvotes: 2