Haskell IORef - an answer vs. a function to get an answer
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I'm trying to understand how IORefs
are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
When printCounts
executes "c <- newCounter
", why doesn't c
get the result of doing the work in the newCounter
"return $ do
" block, which seems like it should get assigned to the constant "IO 0
" the first time it is called and then never change? Instead, c
seems to get assigned the function defined in that "return $ do
" block, which is then executed anew every time printCounts
gets to another "print =<< c
." It seems that the answer somehow lies in newCounter
having the double nested "IO (IO Int)
" type, but I can't follow why that makes c
a function to be re-executed when called instead of a constant evaluated just once.
haskell io closures do-notation ioref
add a comment |
I'm trying to understand how IORefs
are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
When printCounts
executes "c <- newCounter
", why doesn't c
get the result of doing the work in the newCounter
"return $ do
" block, which seems like it should get assigned to the constant "IO 0
" the first time it is called and then never change? Instead, c
seems to get assigned the function defined in that "return $ do
" block, which is then executed anew every time printCounts
gets to another "print =<< c
." It seems that the answer somehow lies in newCounter
having the double nested "IO (IO Int)
" type, but I can't follow why that makes c
a function to be re-executed when called instead of a constant evaluated just once.
haskell io closures do-notation ioref
A side note: you should not have anIO (IO Int)
type, this is bad style.
– AJFarmar
Nov 26 '18 at 19:43
6
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
2
Well, I suppose it would actually be better to provide an abstractCounter
type....
– dfeuer
Nov 26 '18 at 20:10
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
@AJFarmar I wouldn't useIO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.
– Jeremy List
Nov 28 '18 at 20:40
add a comment |
I'm trying to understand how IORefs
are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
When printCounts
executes "c <- newCounter
", why doesn't c
get the result of doing the work in the newCounter
"return $ do
" block, which seems like it should get assigned to the constant "IO 0
" the first time it is called and then never change? Instead, c
seems to get assigned the function defined in that "return $ do
" block, which is then executed anew every time printCounts
gets to another "print =<< c
." It seems that the answer somehow lies in newCounter
having the double nested "IO (IO Int)
" type, but I can't follow why that makes c
a function to be re-executed when called instead of a constant evaluated just once.
haskell io closures do-notation ioref
I'm trying to understand how IORefs
are really used, and I'm having trouble following the sample code I found on https://www.seas.upenn.edu/~cis194/spring15/lectures/12-unsafe.html
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
When printCounts
executes "c <- newCounter
", why doesn't c
get the result of doing the work in the newCounter
"return $ do
" block, which seems like it should get assigned to the constant "IO 0
" the first time it is called and then never change? Instead, c
seems to get assigned the function defined in that "return $ do
" block, which is then executed anew every time printCounts
gets to another "print =<< c
." It seems that the answer somehow lies in newCounter
having the double nested "IO (IO Int)
" type, but I can't follow why that makes c
a function to be re-executed when called instead of a constant evaluated just once.
haskell io closures do-notation ioref
haskell io closures do-notation ioref
edited Nov 26 '18 at 20:18
Will Ness
46.7k469127
46.7k469127
asked Nov 26 '18 at 19:38
David JacobsDavid Jacobs
233
233
A side note: you should not have anIO (IO Int)
type, this is bad style.
– AJFarmar
Nov 26 '18 at 19:43
6
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
2
Well, I suppose it would actually be better to provide an abstractCounter
type....
– dfeuer
Nov 26 '18 at 20:10
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
@AJFarmar I wouldn't useIO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.
– Jeremy List
Nov 28 '18 at 20:40
add a comment |
A side note: you should not have anIO (IO Int)
type, this is bad style.
– AJFarmar
Nov 26 '18 at 19:43
6
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
2
Well, I suppose it would actually be better to provide an abstractCounter
type....
– dfeuer
Nov 26 '18 at 20:10
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
@AJFarmar I wouldn't useIO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.
– Jeremy List
Nov 28 '18 at 20:40
A side note: you should not have an
IO (IO Int)
type, this is bad style.– AJFarmar
Nov 26 '18 at 19:43
A side note: you should not have an
IO (IO Int)
type, this is bad style.– AJFarmar
Nov 26 '18 at 19:43
6
6
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
2
2
Well, I suppose it would actually be better to provide an abstract
Counter
type....– dfeuer
Nov 26 '18 at 20:10
Well, I suppose it would actually be better to provide an abstract
Counter
type....– dfeuer
Nov 26 '18 at 20:10
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
@AJFarmar I wouldn't use
IO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.– Jeremy List
Nov 28 '18 at 20:40
@AJFarmar I wouldn't use
IO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.– Jeremy List
Nov 28 '18 at 20:40
add a comment |
1 Answer
1
active
oldest
votes
You can think of IO
as a type of programs. newCounter :: IO (IO Int)
is a program that outputs a program. More precisely, newCounter
allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter
doesn't execute the program it returns. It would if you wrote instead:
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later
You can also use equational reasoning to unfold printCounts
into a sequence of primitives. All versions of printCounts
below are equivalent programs:
-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
-- by definition of newCounter...
printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))
printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)
printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- let-substitution
printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))
printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3
In the final version, you can see that printCounts
quite literally allocates a counter and increments it three times, printing each intermediate value.
One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ...
is different from x <- p; ...
, which runs p
, and binds x
to the result rather than the program p
itself.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487894%2fhaskell-ioref-an-answer-vs-a-function-to-get-an-answer%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
You can think of IO
as a type of programs. newCounter :: IO (IO Int)
is a program that outputs a program. More precisely, newCounter
allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter
doesn't execute the program it returns. It would if you wrote instead:
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later
You can also use equational reasoning to unfold printCounts
into a sequence of primitives. All versions of printCounts
below are equivalent programs:
-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
-- by definition of newCounter...
printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))
printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)
printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- let-substitution
printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))
printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3
In the final version, you can see that printCounts
quite literally allocates a counter and increments it three times, printing each intermediate value.
One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ...
is different from x <- p; ...
, which runs p
, and binds x
to the result rather than the program p
itself.
add a comment |
You can think of IO
as a type of programs. newCounter :: IO (IO Int)
is a program that outputs a program. More precisely, newCounter
allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter
doesn't execute the program it returns. It would if you wrote instead:
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later
You can also use equational reasoning to unfold printCounts
into a sequence of primitives. All versions of printCounts
below are equivalent programs:
-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
-- by definition of newCounter...
printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))
printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)
printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- let-substitution
printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))
printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3
In the final version, you can see that printCounts
quite literally allocates a counter and increments it three times, printing each intermediate value.
One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ...
is different from x <- p; ...
, which runs p
, and binds x
to the result rather than the program p
itself.
add a comment |
You can think of IO
as a type of programs. newCounter :: IO (IO Int)
is a program that outputs a program. More precisely, newCounter
allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter
doesn't execute the program it returns. It would if you wrote instead:
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later
You can also use equational reasoning to unfold printCounts
into a sequence of primitives. All versions of printCounts
below are equivalent programs:
-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
-- by definition of newCounter...
printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))
printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)
printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- let-substitution
printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))
printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3
In the final version, you can see that printCounts
quite literally allocates a counter and increments it three times, printing each intermediate value.
One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ...
is different from x <- p; ...
, which runs p
, and binds x
to the result rather than the program p
itself.
You can think of IO
as a type of programs. newCounter :: IO (IO Int)
is a program that outputs a program. More precisely, newCounter
allocates a new counter, and returns a program that, when run, increments the counter and returns its old value. newCounter
doesn't execute the program it returns. It would if you wrote instead:
newCounter :: IO (IO Int)
newCounter = do
r <- newIORef 0
let p = do -- name the counter program p
v <- readIORef r
writeIORef r (v + 1)
return v
p -- run the counter program once
return p -- you can still return it to run again later
You can also use equational reasoning to unfold printCounts
into a sequence of primitives. All versions of printCounts
below are equivalent programs:
-- original definition
printCounts :: IO ()
printCounts = do
c <- newCounter
print =<< c
print =<< c
print =<< c
-- by definition of newCounter...
printCounts = do
c <- do
r <- newIORef 0
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- by the monad laws (quite hand-wavy for brevity)
-- do
-- c <- do
-- X
-- Y
-- .....
-- =
-- do
-- X
-- c <-
-- Y
-- .....
--
-- (more formally,
-- ((m >>= x -> k x) >>= h) = (m >>= (x -> k x >>= h)))
printCounts = do
r <- newIORef 0
c <-
return $ do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- c <- return X
-- =
-- let c = X
--
-- (more formally, ((return X) >>= (c -> k c)) = (k X)
printCounts = do
r <- newIORef 0
let c = do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< c
print =<< c
print =<< c
-- let-substitution
printCounts = do
r <- newIORef 0
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
print =<< do
v <- readIORef r
writeIORef r (v + 1)
return v
-- after many more applications of monad laws and a bit of renaming to avoid shadowing
-- (in particular, one important step is ((return v >>= print) = (print v)))
printCounts = do
r <- newIORef 0
v1 <- readIORef r
writeIORef r (v1 + 1)
print v1
v2 <- readIORef r
writeIORef r (v2 + 1)
print v2
v3 <- readIORef r
writeIORef r (v3 + 1)
print v3
In the final version, you can see that printCounts
quite literally allocates a counter and increments it three times, printing each intermediate value.
One key step is the let-substitution one, where the counter program gets duplicated, which is why it gets to run three times. let x = p; ...
is different from x <- p; ...
, which runs p
, and binds x
to the result rather than the program p
itself.
edited Nov 26 '18 at 21:21
answered Nov 26 '18 at 20:05
Li-yao XiaLi-yao Xia
13.3k1430
13.3k1430
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487894%2fhaskell-ioref-an-answer-vs-a-function-to-get-an-answer%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
A side note: you should not have an
IO (IO Int)
type, this is bad style.– AJFarmar
Nov 26 '18 at 19:43
6
@AJFarmar, I don't see anything bad about that style; it's just providing a counter with a restricted interface.
– dfeuer
Nov 26 '18 at 20:03
2
Well, I suppose it would actually be better to provide an abstract
Counter
type....– dfeuer
Nov 26 '18 at 20:10
let-over-lambda to bind-over-return. :)
– Will Ness
Nov 26 '18 at 20:14
@AJFarmar I wouldn't use
IO (IO Int)
just for a counter: but that pattern is often the best way of expressing something.– Jeremy List
Nov 28 '18 at 20:40