haskell: reader-monad for dependency-injection












1















i get the basic functionality of the reader-monad from learnyouahaskell book and i've seen some advises here to make use of it for dependency-injection.
even there are some examples here on stackoverflow, i dont know how to use it for integration-testing.



my code is:



list :: Ctx -> [String] -> IO String
list ctx args = do
d <- eitherDecode <$> Uplink.get (token ctx) (endpointActivities ctx) :: IO (Either String Activities)
case d of
Left err -> return err
Right result -> return $ unlines . filterByPrefix (parsePrefix args) . extractNames $ activities result


uplink.hs



get :: String -> String -> IO B.ByteString
get token endpoint = do
req <- parseRequest endpoint
resp <- httpLBS $ withAuth token req
return $ getResponseBody resp


how can i mock the httpLBS - call for integration-testing with the reader-monad?





EDIT: !!!!



i almost have it now with the reader-monad. the only problem left is that i dont know how i can define that httpsLBS-function in my Ctx-data-type.



httpLBS-function-signature:



httpLBS :: MonadIO m => Request -> m (Response ByteString) 


my Ctx data-type-definition:



data Ctx =
Ctx {
token :: String,
endpointActivities :: String,
endpointTimeTrackingStart :: String,
httpLBSFunc :: MonadIO m => Request -> m (Response ByteString)
} deriving (Show)


i always get the error: Not in scope: type variable ‘m’
how can i define that function in my Ctx-data-type with its constraints?



i promise, that i will post my solution afterwards, when this last problem is solved










share|improve this question




















  • 1





    I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

    – jberryman
    Nov 13 '18 at 16:09











  • In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

    – Mark Seemann
    Nov 13 '18 at 17:02











  • Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

    – chepner
    Nov 13 '18 at 17:03
















1















i get the basic functionality of the reader-monad from learnyouahaskell book and i've seen some advises here to make use of it for dependency-injection.
even there are some examples here on stackoverflow, i dont know how to use it for integration-testing.



my code is:



list :: Ctx -> [String] -> IO String
list ctx args = do
d <- eitherDecode <$> Uplink.get (token ctx) (endpointActivities ctx) :: IO (Either String Activities)
case d of
Left err -> return err
Right result -> return $ unlines . filterByPrefix (parsePrefix args) . extractNames $ activities result


uplink.hs



get :: String -> String -> IO B.ByteString
get token endpoint = do
req <- parseRequest endpoint
resp <- httpLBS $ withAuth token req
return $ getResponseBody resp


how can i mock the httpLBS - call for integration-testing with the reader-monad?





EDIT: !!!!



i almost have it now with the reader-monad. the only problem left is that i dont know how i can define that httpsLBS-function in my Ctx-data-type.



httpLBS-function-signature:



httpLBS :: MonadIO m => Request -> m (Response ByteString) 


my Ctx data-type-definition:



data Ctx =
Ctx {
token :: String,
endpointActivities :: String,
endpointTimeTrackingStart :: String,
httpLBSFunc :: MonadIO m => Request -> m (Response ByteString)
} deriving (Show)


i always get the error: Not in scope: type variable ‘m’
how can i define that function in my Ctx-data-type with its constraints?



i promise, that i will post my solution afterwards, when this last problem is solved










share|improve this question




















  • 1





    I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

    – jberryman
    Nov 13 '18 at 16:09











  • In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

    – Mark Seemann
    Nov 13 '18 at 17:02











  • Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

    – chepner
    Nov 13 '18 at 17:03














1












1








1








i get the basic functionality of the reader-monad from learnyouahaskell book and i've seen some advises here to make use of it for dependency-injection.
even there are some examples here on stackoverflow, i dont know how to use it for integration-testing.



my code is:



list :: Ctx -> [String] -> IO String
list ctx args = do
d <- eitherDecode <$> Uplink.get (token ctx) (endpointActivities ctx) :: IO (Either String Activities)
case d of
Left err -> return err
Right result -> return $ unlines . filterByPrefix (parsePrefix args) . extractNames $ activities result


uplink.hs



get :: String -> String -> IO B.ByteString
get token endpoint = do
req <- parseRequest endpoint
resp <- httpLBS $ withAuth token req
return $ getResponseBody resp


how can i mock the httpLBS - call for integration-testing with the reader-monad?





EDIT: !!!!



i almost have it now with the reader-monad. the only problem left is that i dont know how i can define that httpsLBS-function in my Ctx-data-type.



httpLBS-function-signature:



httpLBS :: MonadIO m => Request -> m (Response ByteString) 


my Ctx data-type-definition:



data Ctx =
Ctx {
token :: String,
endpointActivities :: String,
endpointTimeTrackingStart :: String,
httpLBSFunc :: MonadIO m => Request -> m (Response ByteString)
} deriving (Show)


i always get the error: Not in scope: type variable ‘m’
how can i define that function in my Ctx-data-type with its constraints?



i promise, that i will post my solution afterwards, when this last problem is solved










share|improve this question
















i get the basic functionality of the reader-monad from learnyouahaskell book and i've seen some advises here to make use of it for dependency-injection.
even there are some examples here on stackoverflow, i dont know how to use it for integration-testing.



my code is:



list :: Ctx -> [String] -> IO String
list ctx args = do
d <- eitherDecode <$> Uplink.get (token ctx) (endpointActivities ctx) :: IO (Either String Activities)
case d of
Left err -> return err
Right result -> return $ unlines . filterByPrefix (parsePrefix args) . extractNames $ activities result


uplink.hs



get :: String -> String -> IO B.ByteString
get token endpoint = do
req <- parseRequest endpoint
resp <- httpLBS $ withAuth token req
return $ getResponseBody resp


how can i mock the httpLBS - call for integration-testing with the reader-monad?





EDIT: !!!!



i almost have it now with the reader-monad. the only problem left is that i dont know how i can define that httpsLBS-function in my Ctx-data-type.



httpLBS-function-signature:



httpLBS :: MonadIO m => Request -> m (Response ByteString) 


my Ctx data-type-definition:



data Ctx =
Ctx {
token :: String,
endpointActivities :: String,
endpointTimeTrackingStart :: String,
httpLBSFunc :: MonadIO m => Request -> m (Response ByteString)
} deriving (Show)


i always get the error: Not in scope: type variable ‘m’
how can i define that function in my Ctx-data-type with its constraints?



i promise, that i will post my solution afterwards, when this last problem is solved







haskell dependency-injection






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 14 '18 at 21:49







stefa ng

















asked Nov 13 '18 at 15:38









stefa ngstefa ng

7416




7416








  • 1





    I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

    – jberryman
    Nov 13 '18 at 16:09











  • In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

    – Mark Seemann
    Nov 13 '18 at 17:02











  • Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

    – chepner
    Nov 13 '18 at 17:03














  • 1





    I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

    – jberryman
    Nov 13 '18 at 16:09











  • In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

    – Mark Seemann
    Nov 13 '18 at 17:02











  • Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

    – chepner
    Nov 13 '18 at 17:03








1




1





I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

– jberryman
Nov 13 '18 at 16:09





I'm not sure I know what dependency injection really is, but why not parameterize get by the http client function, like: get myHttpLBS token endpoint = do .... Reader is an abstraction around argument passing. I don't tend to reach for it unless both of: 1) I already have a monad stack, and 2) I have multiple top-level functions that call each other, and in particular I'd like to pass some value through a function, i.e. there is some function that does nothing but pass an argument to some other function. Other folks probably feel differently

– jberryman
Nov 13 '18 at 16:09













In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

– Mark Seemann
Nov 13 '18 at 17:02





In functional programming, you can't use dependency injection because it makes everything impure. Instead, you compose functions, keeping as many of them pure as possible.

– Mark Seemann
Nov 13 '18 at 17:02













Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

– chepner
Nov 13 '18 at 17:03





Did you write uplink.hs? Don't hard-code httpLBS; make it a parameter.

– chepner
Nov 13 '18 at 17:03












1 Answer
1






active

oldest

votes


















0














In Haskell, dependency injection is just higher order programming + currying. You can write your code as the following.



-- where (? -> ?) is the type of httpLBS
get_ :: (? -> ?) -> String -> String -> IO B.ByteString
get_ httpFunc token endpoint = do
req <- parseRequest endpoint
resp <- httpFunc $ withAuth token req
return $ getResponseBody resp

getProduction = get_ httpLBS

getTest = get_ httpMock





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284484%2fhaskell-reader-monad-for-dependency-injection%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    In Haskell, dependency injection is just higher order programming + currying. You can write your code as the following.



    -- where (? -> ?) is the type of httpLBS
    get_ :: (? -> ?) -> String -> String -> IO B.ByteString
    get_ httpFunc token endpoint = do
    req <- parseRequest endpoint
    resp <- httpFunc $ withAuth token req
    return $ getResponseBody resp

    getProduction = get_ httpLBS

    getTest = get_ httpMock





    share|improve this answer




























      0














      In Haskell, dependency injection is just higher order programming + currying. You can write your code as the following.



      -- where (? -> ?) is the type of httpLBS
      get_ :: (? -> ?) -> String -> String -> IO B.ByteString
      get_ httpFunc token endpoint = do
      req <- parseRequest endpoint
      resp <- httpFunc $ withAuth token req
      return $ getResponseBody resp

      getProduction = get_ httpLBS

      getTest = get_ httpMock





      share|improve this answer


























        0












        0








        0







        In Haskell, dependency injection is just higher order programming + currying. You can write your code as the following.



        -- where (? -> ?) is the type of httpLBS
        get_ :: (? -> ?) -> String -> String -> IO B.ByteString
        get_ httpFunc token endpoint = do
        req <- parseRequest endpoint
        resp <- httpFunc $ withAuth token req
        return $ getResponseBody resp

        getProduction = get_ httpLBS

        getTest = get_ httpMock





        share|improve this answer













        In Haskell, dependency injection is just higher order programming + currying. You can write your code as the following.



        -- where (? -> ?) is the type of httpLBS
        get_ :: (? -> ?) -> String -> String -> IO B.ByteString
        get_ httpFunc token endpoint = do
        req <- parseRequest endpoint
        resp <- httpFunc $ withAuth token req
        return $ getResponseBody resp

        getProduction = get_ httpLBS

        getTest = get_ httpMock






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 13 '18 at 18:46









        Izaak WeissIzaak Weiss

        671510




        671510






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53284484%2fhaskell-reader-monad-for-dependency-injection%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Full-time equivalent

            Bicuculline

            さくらももこ