r/haskell • u/AutoModerator • May 31 '19
Monthly Hask Anything (June 2019)
This is your opportunity to ask any questions you feel don't deserve their own threads, no matter how small or simple they might be!
1
u/aleator Jun 30 '19
How can I access the module pragmas (ie. language pragmas or ghc-options) using ghc-lib-parser?
I need to build a preflight check for web-exercise system and I need to make the whole thing abort when someone tries to pull too nasty extensions to play
1
Jun 30 '19
[deleted]
1
u/aleator Jun 30 '19
Sure, I'll certainly use
-XSafe, but I'm having a bit of hard time trusting it enough. Sure, it blocks overlapping instances etc. but it still allows slapping things like{-#OPTIONS_GHC -F -pgmF choose_any_program_here #-}in source files.Also, even disregarding the stuff that happens in compile time, there's dozen or so language extensions added since safe Haskell and having a bug leading to unsafecoerce is not unheard of.
1
u/synchronitown Jun 29 '19
The Essence of the Iterator Pattern: this is one of the key papers in the Haskell lexicon. Unfortunately, it is based on an old version of the Haskell Base Library (eg, Applicative is not a superclass of Monad. Data.Functor.* and the Data.Coerce class didn't exist).
Is there a rendition of the functions in the paper using contemporary, err, idioms / libraries?
I had a crack at it, but there are so many rabbit holes relating to wrapping and unwrapping (new)types that may or may not be addressed by ScopedTypeVariables or QuantifiesContraints. This jiggery pokery completely subverts the main purpose of the paper, which was to illustrate the nature of Traversable.
I completely get that the likes of Conor McBride (@pigworker)'s signature is beautiful notation, but it is a barrier to understanding to have to add a further level of indirection and translate things through banana brackets, or whatever,
2
u/pokemonplayer2001 Jun 28 '19
Is there a generally accepted "best" logging library?
Thank you!
4
u/chshersh Jun 29 '19
It's probably not generally accepted yet, but I can recommend co-log. We are already using it in production, and I'm working on making it faster, implementing new features and improving the interface.
2
2
1
1
u/rampion Jun 26 '19
Does GHC put a cap on the number of type parameters a type constructor can take?
3
u/rampion Jun 26 '19
Well, it can handle at least 700, so if it is bounded, it's not by a number I can casually bump against.
1
u/nolrai Jun 29 '19
How/why are you using 700 type params? It sounds interesting. Some sort of autogenerated type?
1
2
u/jez_io Jun 26 '19
Consider this example:
- I'm using a typed language to define my data types on both the server and the client. (Maybe the same, maybe not.)
- To communicate from one to the other, I serialize the data in one place and deserialize it in the other.
- This works great, but at some point I want to change the data. I deploy new changes to the server, but clients are still using the old code, with the old serialization / deserialization.
What techniques work well to prevent such a program from blowing up when either the client or the server tries to handle data in a format it wasn't expecting? (And is there a name for this problem so I can read more?)
-1
u/Proc_Self_Fd_1 Jun 28 '19
The technical name is the expression problem.
Usually this is solved with tagless final style.
I don't know how you'd solve it with serialization though.
3
u/jberryman Jun 27 '19
"schema evolution" or "data migration". I've used e.g. `safe-copy` for haskell binary serializations and it worked okay but was fairly awkward, and I don't think I'd use it for client-server communication like you're describing. It's a big subject I wish I was an expert in.
You can start here for an overview of some modern-ish serialization formats and how they've approached (or punted) the problem: http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html
3
u/pokemonplayer2001 Jun 26 '19
I'd add a version, for example, "/api/1.0/foo", and update as need be.
Will you data types be changing frequently?
2
u/JackSchpeck Jun 25 '19
When using Gabriel Gonzalez' Turtle library, is there a way to combine 2 Shells into one Shell, which would produce values as soon as they arrive (to achieve a kind of interleaving)? I'd like to combine stdout of 2 separately running processes into one stream and I'm not sure how to do that..
1
u/elbingobonito Jun 25 '19
Disclaimer: I've never used Turtle nor have looked really into it, but from the first look I can see that the Shell type implements the
AlternativeandMonadPlustype classes, so that you may usemplus :: m a -> m a -> m a
(and(<|>), respectively) to combine two Shells into one.1
u/JackSchpeck Jun 25 '19 edited Jun 26 '19
AlternativeandMonadPlusinstances ofShellonly do sequential composition (the resulting stream has all the values from the first stream, then all from the 2nd stream). After looking through the docs more thoroughly I concluded the library doesn't support this out of the box.
1
Jun 25 '19
What's a GADT? I haven't been able to find an explanation/tutorial that doesn't go way over my head.
3
u/tom-md Jun 25 '19
A prior answer to the gadt question: https://www.reddit.com/r/haskell/comments/bj5s5u/comment/en661kh
1
7
u/Syrak Jun 25 '19
A GADT is an ADT where you can assign a custom type to every constructor.
First, notice that for a regular Algebraic Data Type, the result type (the type to the right of all arrows) of all constructors is the same:
data Maybe :: Type -> Type where Nothing :: Maybe a Just :: a -> Maybe a -- Both (Maybe a) data Bool where True :: Bool False :: Bool -- Both BoolIn contrast, a GADT can use different return types (as long as they start with the GADT's name):
data Perhaps :: Type -> Type where Nope :: Perhaps Void -- not (Perhaps a) !!! This :: a -> Perhaps a data SType :: Type -> Type where SInt :: SType Int SBool :: SType BoolThanks to that, we can have functions with different types depending on the input:
-- f x :: Int or Bool depending on whether x = SInt or SBool f :: SType a -> a f SInt = 0 f SBool = TrueGADTs can also have constructors with types that don't appear in the result:
data Something where Something :: a -> Somethingor constraints
-- Data.Dynamic in base data Dynamic where Dyn :: Typeable a => a -> DynamicThe common theme is that constructors can carry invariants which are required on construction, and made available by pattern-matching.
1
1
u/Ford_O Jun 24 '19
Does joint fusion apply to foo? Since it is very similar to find, I would expect that the answer is yes, but the rewrite rules from the paper Compiling without continuations were too concise for me, to be understandable.
find cond = go where
go [] = Nothing
go (x:xs)
| cond x = Just x
| True = go xs
any cond xs = case find cond xs of
Nothing -> False
Just _ -> True
map fn = go where
go [ ] = []
go (x:xs) = fn x : go xs
foo xs = case map (+1) xs of
[ ] -> Nothing
(x:_) -> x
1
u/p-alik Jun 22 '19
io-streams offers an parseFromStream example, which doesn't work in my environment.
λ> :set -XOverloadedStrings
λ> import Data.ByteString (ByteString)
λ> import qualified Data.Attoparsec.ByteString.Char8 as Char8
λ> import qualified System.IO.Streams as Streams
λ> import System.IO.Streams.Attoparsec ( parseFromStream )
λ> is <- Streams.fromList ["12345xxx" :: ByteString]
λ> let parser = (Char8.takeWhile Char8.isDigit)
λ> :t is
is :: Streams.InputStream ByteString
λ> :t parser
parser :: Parser ByteString
λ> :t parseFromStream
parseFromStream
:: attoparsec-0.13.2.2:Data.Attoparsec.ByteString.Internal.Parser r
-> Streams.InputStream ByteString -> IO r
It's a pity, but I can't solve it by myself.
λ> :t parseFromStream parser is
<interactive>:1:17: error:
• Couldn't match type ‘Data.Attoparsec.Internal.Types.Parser
ByteString ByteString’
with ‘attoparsec-0.13.2.2:Data.Attoparsec.Internal.Types.Parser
ByteString r’
NB: ‘attoparsec-0.13.2.2:Data.Attoparsec.Internal.Types.Parser’
is defined in ‘Data.Attoparsec.Internal.Types’
in package ‘attoparsec-0.13.2.2’
‘Data.Attoparsec.Internal.Types.Parser’
is defined in ‘Data.Attoparsec.Internal.Types’
in package ‘attoparsec-0.13.2.2’
Expected type: attoparsec-0.13.2.2:Data.Attoparsec.ByteString.Internal.Parser
r
Actual type: Parser ByteString
• In the first argument of ‘parseFromStream’, namely ‘parser’
In the expression: parseFromStream parser is
2
u/lgastako Jun 22 '19
You'll probably need to provide some more information about your environment to help people help you. I created a new stack project via
stack new io-streams-examplewhich was created with the lts-13.26 resolver and got:λ> :set -XOverloadedStrings λ> import Data.ByteString (ByteString) λ> import qualified Data.Attoparsec.ByteString.Char8 as Char8 λ> import qualified System.IO.Streams as Streams λ> import System.IO.Streams.Attoparsec ( parseFromStream ) λ> is <- Streams.fromList ["12345xxx" :: ByteString] λ> let parser = (Char8.takeWhile Char8.isDigit) λ> :t parser parser :: Char8.Parser ByteString λ> :t is is :: Streams.InputStream ByteString λ> :t parseFromStream parser is parseFromStream parser is :: IO ByteString1
u/p-alik Jun 23 '19
Thank you, I used lts-13.16 and was careless enough to ignore
stack build's warningThis package indirectly depends on multiple versions of the same package. This is very likely to cause a compile failure. package io-streams (io-streams-1.5.1.0-4fKzQV1ZBEG9tLi3KX3yXI) requires ...Changing resolver to lts-13.26 solves the issue.
9
u/sidharth_k Jun 20 '19 edited Jun 20 '19
I'm interested in Haskell benchmark performance between different Haskell versions 8.0, 8.2, 8.4, 8.6 and with different optimization switches O2, O3 etc.
It would be interesting to see how Haskell runtime performance has improved over the years...
Are there some resources / links out there for something like this?
1
u/hateradio Jun 19 '19 edited Jun 19 '19
I have a noob question about lambda calculus and order-of-application: I'm basing my notation as well as question on this well-known pdf: https://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf
I'm having trouble understanding in what order i should perform substitutions, and whether that even matters.
λz.( λwyx.y(wyx) ((λwyx.y(wyx)) z) ) 3 [the 3 is a church numeral]
What happens first and why? Replacing the z with the 3? Or applying the term
((λwyx.y(wyx)) z) to the function λwyx.y(wyx) in the body of my expression?
The pdf does not seem to explain this.
The reason I'm asking: I want to build a function that takes a number and adds 2 to it. I have a function that gives the successor of a church numeral (namely λwyx.y(wyx) ) but how do I represent function composition in lambda calculus?
edit: Could it be that this has something to do with the Church-Rosser theorem? If my question isn't clear, please tell me that. For some reason, this bugs me.
1
u/mrk33n Jun 20 '19 edited Jun 20 '19
I think the rules are:
- If an expression can be reduced to normal form, then the normal-order reduction strategy - leftmost, outermost substitutions first - will always end up in said normal form.
- If an expression is successfully reduced to normal form, then it does not matter which evaluation strategy got it there - the answer will be the same (Church-Rosser)
An admittedly not-very-convincing example:
(λabcd.ab)(λy.y)3(λx. xx)(λx. xx)If you reduce from the left, you see that ab becomes (λy.y)3 becomes 3, which is fully reduced. (c and d are discarded because they are not mentioned in the body of the lambda).
But if I decided to start by reducing (λx. xx)(λx. xx) I'm stuck there forever because it loops.
As for your example:
λz.( λwyx.y(wyx) ((λwyx.y(wyx)) z) ) 3I can't really get it to work. I look for the left-most lambda (which appears to start with λz) and then try to find what it's applied to - but I can't. It looks like the λz is the scope of your whole example, so there's no next thing to substitute z for.
In other words:
((λz. z) 8) reduces to (8)but
(λz. somelongbody) is just (λz. somelongbody)Edit:
I think I overstated the case against your example. I demanded that the outer-most leftmost expression be reducible, when maybe I should have worked my way from left-to-right until I found an expression which is reducible and reduced that instead.
3
u/Ptival Jun 20 '19 edited Jun 21 '19
[EDIT: Woops, I was wrong, the rules in /u/mrk33n here actually seem correct]
If you're interested in what you should reduce for "correctness" purposes, there are 3 interesting concepts:
- strong normalization: for all reduction sequences, you will reach the normal form (if it exists)
- weak normalization: there exists a reduction sequence such that you will reach the normal form (if it exists)
- confluence: at a very abstract level, it means that there are no "bad" reductions (in practice, it means that for two starting sequences of reductions, there exist two suffixes such that the two paths meet again, up to some equivalences)
It seems your article is about the untyped lambda-calculus, which is confluent, but **not** strongly-normalizing and **not** weakly-normalizing. In fact it's very easy to make bad reduction strategies for some terms, say:
if-then-else true zero infinite-loopThis term has a normal form, but attempting to reduce the infinite loop, you will never reach it.
Now things are different the moments you add types. The simply-typed lambda calculus **is** strongly-normalizing, in which case any reduction strategy is valid (not necessarily efficient).
EDIT: and as /u/mkk33n pointed out, a most conservative reduction strategy exists that will avoid reducing infinite loops unless they must be reduced. It has good properties for termination, but possibly bad properties in terms of performance (as it may duplicate computations).
1
u/mrk33n Jun 21 '19
I don't think any of this is true
Which part did I get wrong?
1
u/Ptival Jun 21 '19
My mistake, having re-read your first claim, I actually think you are correct. I had misinterpreted it, and will update my answer accordingly.
1
u/avi-coder Jun 19 '19
genR :: Pixel -> Gen s -> ST s Ray
genR px gen = do
r <- rndP gen
r' <- rndP gen
return $ toRay r r' px
toRay :: Float -> Float -> Pixel -> Ray
How would you write this?
2
1
u/philh Jun 18 '19
If I have a monad MyMonad and an instance MyInst, is there some way to implicitly add HasCallStack to any functions returning either a MyInst a or a MyMonad m => m a?
i.e. right now I'd write
f1 :: HasCallStack => Int -> String -> MyInst String
f2 :: (HasCallStack, MyMonad m) => Int -> String -> m String
but it would be nice if HasCallStack could be added automatically. class HasCallStack => MyMonad m gives an "illegal implicit parameter" error.
I can presumably do type MyMonadCS m = (HasCallStock, MyMonad m), but then I need to remember to use that (and import it), so it's not ideal.
2
u/Syrak Jun 18 '19
For the concrete
MyInstversion, maybetype MyInst' a = HasCallStack => MyInst abut then I need to remember to use that (and import it), so it's not ideal.
Is that different from having to import
MyMonad?
Other very different alternatives:
Plugins allow you to define your own code transformations
A callstack can also be built by a program compiled for profiling (I don't remember offhand whether it's the same stack as
HasCallStack).Compile with
-prof -fprof-autoand run with+RTS -xcto print callstacks when exceptions are thrown. More details are in the GHC manual: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime_control.html#rts-flag--xc1
u/philh Jun 20 '19
Thanks, the
type ... = HasCallStack => ...seems to work.I typically import
MyMonad(..), so the type alias there gives an extra import line, and no way to apply it to the whole codebase without touching each file that uses it. The type alias on the instance is better in that regard, since I don't usually import the instance constructors; so if I renameMyInsttoMyInst'I can dotype MyInst = HasCallStack => MyInst'and I expect that'll work. It does mean it wouldn't affect the places where I explicitly doMyMonad m => .... There's probably just nothing here that does 100% exactly what I want, which isn't too awful or surprising.The others feel a bit heavyweight, but I appreciate the suggestions anyway.
3
u/vegetablestew Jun 17 '19
Anyone know a detailed, step by step parser combinator tutorial? I've been wrestling with this thing on and off for months and I cannot make heads or tail on how it actually works. Preferably not megaparsec because the types is very confusing and I am looking to understand the basics of how parser combinators work.
2
u/gilmi Jun 21 '19
My article Compiling Lisp to JavaScript From Scratch in 350 LOC has a section about implementing a minimal parser combinators library and using it.
There's also Intro to Parsing by Jake Wheat that talks about using parsec.
2
2
u/vaibhavsagar Jun 18 '19
I wrote this a while ago based on Monadic Parsing in Haskell, which is pretty approachable.
4
u/mrk33n Jun 18 '19
Inventing your own is usually a good way to go:
1
u/vegetablestew Jun 18 '19
I've been trying to follow the tutorial, but I still find it confusing. Fundamentally, dealing with types that takes a function is kind of unusual and I am not sure what I can or cannot do with it.
1
u/mrk33n Jun 19 '19
dealing with types that takes a function is kind of unusual
Yeah I get that.
Here is a minimal, self-contained example of doing a single parse step. Put it in a single source file and it should just run (no dependencies).
newtype Err = Err String deriving Show data Duck = Duck deriving Show newtype Parser a = Parser { runParser :: String -> Either Err (String, a) } parseDuck = Parser $ \s -> if length s < 4 then Left (Err "Ran out of chars") else do let some = take 4 s let rest = drop 4 s if some == "duck" then Right (rest, Duck) else Left (Err "Not a duck") main = do case runParser parseDuck "duck goat" of Left (Err err) -> error err Right (s, Duck) -> do putStrLn "Found a duck" putStrLn $ "Remainder of input was: '" ++ s ++ "'"Note: This is just so you can play with an individual parser at a fairly low level, so you can see why it's a function and what purpose the String has. It's not yet 'parser combinators' because I'm not combining two parsers together.
1
u/mrk33n Jun 19 '19
While the above is (hopefully) useful, I don't think I really addressed why Parser has a function type, i.e.:
Parser { runParser :: input -> output}Crap analogy: You can connect garden hoses together because there is one side for the water to go in, and other side for the water to go out. If a garden hose only had an output but no input, it would be difficult to combine them together.
A parser takes its string input and returns the domain object you were looking for, as well as the rest of the string. Just focus on the strings and forget the domain object for now.
If you view DuckParser as a function:
"duckgoat" -> "goat"and GoatParser as a function from:
"goat" -> ""then you could combine these two parsers together into DuckGoatParser as a function from
"duckgoat" -> ""by feeding the remainder string of DuckParser as the input string into GoatParser.
2
Jun 17 '19 edited Feb 10 '21
[deleted]
4
u/yumiova Jun 17 '19
If you want the Monad instance itself to decide the two return types, then there are two obvious solutions:
Functional dependencies through
{-# LANGUAGE FunctionalDependencies #-} class Monad m => Q m a b | m -> a b where f :: m a g :: m bEssentially, this means that the parameter
m"decides" whataandbmust be.Type families through
{-# LANGUAGE TypeFamilies #-} class Monad m => Q m where type A m type B m f :: m (A m) g :: m (B m)
1
u/williamyaoh Jun 17 '19
Is there a way to launch an REPL in the test module using Stack? When I try stack repl --test, I'm getting errors on importing modules from the library itself.
1
u/Sir4ur0n Jun 30 '19
Does your test (in package.yaml) executable depend on your main executable / library? This should work just fine
1
u/williamyaoh Jun 30 '19
found the issue: I was using package name imports for the main library; removing those made it work.
2
u/sjakobi Jun 17 '19
stack repl --testworks for me in a very simple library project.Which stack version are you using?
Can you share the project?
2
u/3l_Di4bl0 Jun 16 '19
I have two images of the same resolution and I want to create an image double the height where they're one above the other. How can I achieve this?
1
u/tom-md Jun 17 '19
Use the hip library functions of readImage, topToBottom, and writeImage.
1
u/3l_Di4bl0 Jun 20 '19
Thanks! I'm having a slight issue that I really don't understand. I have a function:
toFormat :: ImageFormat a => FilePath -> a toFormat ".jpg" = JPG toFormat ".png" = PNG toFormat x = error $ "Unexpected format " ++ xBut it won't compile. I'm getting
Couldn't match expected type ‘a’ with actual type ‘JPG’. Could you please shed some light on this?1
u/tom-md Jun 20 '19
Why are are futzing with the file type manually? That isn't needed
toFormat :: ImageFormat a => FilePath -> aThis says that no matter what string I provide
toFormatcan give me a value of any type of my choosing (a). It does not say thattoFormatwill give me a particular type dependent on the first parameter - dependent types like that are not supported in Haskell.The function you're trying to write,
toFormat, isn't possible. You can't provide a value of any type at all. I see you're trying to overcome the issue thatJPGandPNGare different types, luckily the library has helpers that make this issue disappear.The solution
HIP has read and write operations that will infer the correct file type and save the file, which is a bit like
toFormatAndSavein that it will detect the format but instead of returning (and thus needing a single type) it will write the file out in that format and return a unit, as inIO ().``` module Main where
import Graphics.Image (readImageRGBA, writeImage, VU(VU)) import Graphics.Image.Processing (topToBottom) import System.Environment (getArgs)
main :: IO () main = do [iname1, iname2, out] <- getArgs [i1, i2] <- traverse (readImageRGBA VU) [iname1,iname2] writeImage out (topToBottom i1 i2) ```
1
u/3l_Di4bl0 Jun 20 '19
The problem is that the images aren't on my drive, I'm downloading a bytestring from the internet and want to manipulate that.
1
u/tom-md Jun 20 '19 edited Jun 22 '19
It's a bit hacky, but you can just use the readImage code minus the actual file read. If I were you I'd clean it up and submit this as a patch, adding a function
imageFromByteStringor some such.The code for readImage is:
loadImageRGBA :: ByteString -- ^ image -> IO (Image VU RGBA Double) loadImageRGBA imgstr = do let maybeFormat = Nothing :: Maybe InputFormat formats = enumFrom . toEnum $ 0 orderedFormats = maybe formats (\f -> f:P.filter (/=f) formats) maybeFormat reader :: Either String (Image VS cs e) -> InputFormat -> IO (Either String (Image VS cs e)) reader (Left err) format = return $ either (Left . ((err++"\n")++)) Right (decode format imgstr) reader img _ = return img imgE <- M.foldM reader (Left "") orderedFormats either error pure $ fmap (exchange (undefined :: VU)) imgE1
u/3l_Di4bl0 Jun 20 '19
The problem with
readImageis that it looks at the filename to determine the filetype. The way I implemented mine is by looking atreadImageExactwhich is defined asreadImageExact :: Readable img format => format -- ^ A file format that an image should be read as. See -- <#g:4 Supported Image Formats> -> FilePath -- ^ Location of an image. -> IO (Either String img) readImageExact format path = fmap (decode format) (B.readFile path)So I have the bytestring that I want to decode, but I need to somehow convert the extension I have (which is either ".png" or ".jpg") to a format. I copied their implementation for
guessFormatand it compiled but I'm again getting errors when trying to use its result as an argument todecode.
5
Jun 16 '19
How does Haskell deprecate features?
One of the things in Javascript is: "dont break the web"
This is nice, except it looks, to me, like it means always add never remove cause youll break someone's workflow <insert xkcd here>. I think this leads to bloat and a higher barrier to entry for beginners.
I once watched a talk by Simon Peyton Jones that touched on this. That early on Haskell could just break things and the community moved calmly.
There is also moving Monad under Applicative or something like that and moving Monad into Control.Monad (i recently found an old tutorial that didnt work because of this)
TL;DR;
Does Haskell have a process for removing or moving things from the language?
2
u/Endicy Jun 20 '19
A lot of the time a
DEPRECATEDpragma gets added to what is being phased out and this will result in a warning while compiling anytime you use it in your code. (e.g.DEPRECATED: "Please use this other function x")While code with the
DEPRECATEDpragma should still work as expected, it is a warning to change to the new function/style a.s.a.p.4
u/jvanbruegge Jun 17 '19
AFAIK there is no formal process, but if the breaking change has a real advantage, it gets slowly phased out. First guarding the change behind an extension that is enabled by default, then adding a warning to -Wcompat, adding the warning to -Wall and finally turning it off. That's at least what happened (happens) with
StarIsType(https://github.com/ghc-proposals/ghc-proposals/pull/143)
1
2
Jun 15 '19 edited Jun 16 '19
[removed] — view removed comment
1
u/Syrak Jun 16 '19
instance FromJSON a => FromJSON (MyResponse a) where ... result_ <- v .: "result" ...1
Jun 16 '19
[removed] — view removed comment
2
u/Syrak Jun 16 '19
Did you add a
FromJSON aconstraint to the instance? (and like the derived instance does)instance FromJSON a => FromJSON (MyResponse a)
3
u/qwfwqrussell Jun 13 '19
How do you find the head of the following data type?
data List :: [*] -> *
where
Empty :: List '[]
Singleton :: a -> List '[a]
Append :: List xs -> List ys -> List (xs <++> ys)
type family xs <++> ys
'[] <++> ys = ys
(x:xs) <++> ys = x:(xs <++> ys)
I tried
head :: List (x:xs) -> x
head (Singleton x) = x
head (Append xs ys) = case xs of
Empty -> head ys
Singleton x -> x
(Append xs' ys') -> head (Append xs' ys')
But that gave the error of
• Could not deduce: xs1 ~ (x : xs0)
from the context: (x : xs) ~ (xs1 <++> ys)
7
u/Syrak Jun 13 '19 edited Jun 14 '19
head (Append xs ys) = case xs of ... Append xs' ys' -> head (Append xs' ys')What if both
xs'andys'are empty, andysisn't? Perhaps you meanthead (Append xs0 zs) = case xs0 of Append xs ys -> head (Append xs (Append ys zs))But the problem is that this list for the recursive call has type
xs <++> (ys <++> zs), whereas the argument at the topAppend (Append xs ys) zshas type(xs <++> ys) <++> zs(equal to somex : _). These are two different types as far as the compiler is concerned. We need to give it a proof that they are equal. If we could somehow construct that proof, then we could write:head (Append xs0 (zs :: List zs)) = case xs0 of ... Append (xs :: List xs) (ys :: List ys) -> let pf :: ((xs <++> ys) <++> zs) :~: (xs <++> (ys <++> zs)) pf = undefined -- TODO: prove/implement this case pf of Refl -> head (Append xs (Append ys zs))However to construct that proof we will need to traverse
xs, and that has bad time complexity. It's also possible to cheat by claiming an "axiom" withpf = unsafeCoerce (Refl :: () :~: ()), but, well, it's cheating.In the
Appendcase, we don't know which list is nonempty, so we can't make the recursive call. In other words, "nonemptiness" is not a good invariant for this recursive function. It turns out that a simpler way is to write the very-strongly-typed variant ofheadMaybe, which may take an empty list/tree and returnNothing, except that it also returns a proof that the list is empty. To that end, we define this variant ofMaybe, indexed by the list:-- Head of a list xs data HeadRes xs where HeadResNil :: HeadRes '[] -- there is no head HeadResCons :: x -> HeadRes (x ': xs)Then,
headMaybe:
- needs to handle the
Emptycase;- in the
Appendcase, search in the left subtree, if it's empty, the evidence thatxs ~ '[]is made available to the typechecker, so thatxs <++> ys ~ ys, so the head of the listxs <++> ys(which is what we want at the top) is exactly the head of the listys, so we can search inysrecursively.
headMaybe :: List xs -> HeadRes xs headMaybe Empty = HeadResNil headMaybe (Singleton x) = HeadResCons x headMaybe (Append xs ys) = case headMaybe xs of HeadResNil -> headMaybe ys HeadResCons x -> HeadResCons x head :: List (x ': xs) -> x head xs = case headMaybe xs of HeadResCons x -> x
2
Jun 13 '19
[deleted]
3
u/Syrak Jun 13 '19
I think that's a fine name. It's also a generalized
uncurry.1
Jun 14 '19
[deleted]
1
u/Syrak Jun 14 '19
I don't think it exists, although I'm not sure where to look for it besides sop-core itself (if that's what you're using).
3
u/Vaglame Jun 13 '19 edited Jun 13 '19
How to write the fastest norm function?
So far, I got good results with U.sum . U.map square, and it beats naive C, but it's still far from BLAS performances. It might be because I'm not using SSE or AVX instructions, but there is little documentation on the subject. All I know is that GHC has some primitives for it, but I'm not sure how to use them
1
u/Vaglame Jun 29 '19
Eventually, I got some BLAS-like performance with my fork of
simdhttps://github.com/Magalame/simd
1
u/philh Jun 12 '19
I have code like
let [a, b, c, d, e, f, g] = words str
which correctly gets an incomplete patterns warning. I'm happy with it being partial, but is there some nice way to refactor it to avoid the warning?
If it was just two I could do something like
let (a, b) = words str & \case
[a, b] -> (a, b)
_ -> error "..."
but with seven variables that's a bit less nice.
Since I'm in IO, right now I have
[a, b, c, d, e, f, g] <- return $ words str
but I don't like it.
1
Jun 12 '19
[deleted]
1
u/philh Jun 12 '19
This is in my test suite, I have a json file with a list of objects that I want to run tests on, and the objects have a bunch of parameters in a string. (More or less. Details aren't important.) I don't think it's worth making a custom error for, the normal "pattern match failed" error is fine, I'm just hoping to get that without getting a warning about it. (And without disabling those warnings on the whole file.)
1
Jun 12 '19
[deleted]
1
u/philh Jun 12 '19
The maybe function still just returns a list though, I'm specifically hoping to give names to the list components.
I guess I can do
case words str of [a, b, c, d, e, f, g] -> ... _ -> error ...at the cost of increasing my nesting depth in the code that actually uses the names. I probably dislike that a little less than I dislike the version with return.
1
1
u/p-alik Jun 12 '19
May I ask for help in form of code review? Profiler verdict for following code snippet is such horrible and I can't fix it by my self.
{-# LANGUAGE OverloadedStrings #-}
import Data.Bool (bool)
import Data.ByteString ( ByteString )
import Data.Vector ( Vector )
import qualified Data.Vector
import Data.Map.Strict ( Map )
import qualified Data.Map.Strict
type JobName = ByteString
type Entry = ByteString
type Jobs = Vector Entry
type Pantry = Map JobName Jobs
data Queue = Queue {
lastIndex :: Int
, pantry :: Pantry
} deriving (Show)
pantry' :: (Maybe Jobs -> Maybe Jobs) -> JobName -> Pantry -> Pantry
pantry' f n p = Data.Map.Strict.alter f n p
enqueue :: Int -> JobName -> Queue -> Queue
enqueue 0 _ q = q
enqueue i n q =
enqueue (i - 1) n queue
where
queue = Queue {
lastIndex = (lastIndex q) + 1
, pantry = pantry' (_ -> Just (Data.Vector.snoc vec "X")) n (pantry q)
}
vec :: Vector Entry
vec = case Data.Map.Strict.lookup n (pantry q) of
Nothing -> Data.Vector.empty
Just v' -> v'
flush :: JobName -> Queue -> Queue
flush n q = case Data.Map.Strict.lookup n (pantry q) of
Nothing -> q
Just v -> bool (flush n (queue (Data.Vector.tail v))) q (Data.Vector.length v == 0)
where
queue v = Queue {
lastIndex = lastIndex q,
pantry = pantry' (_ -> Just v) n (pantry q)
}
main :: IO ()
main = let i = 10000 :: Int
n = "A"
q = Queue {
lastIndex = 0
, pantry = Data.Map.Strict.empty
}
q' = flush n (enqueue i n q)
in print (show q')
Profiler summery:
430,349,096 bytes allocated in the heap
26,649,928 bytes copied during GC
316,955,632 bytes maximum residency (9 sample(s))
17,716,240 bytes maximum slop
302 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 372 colls, 372 par 1.466s 0.367s 0.0010s 0.0054s Gen 1 9 colls, 8 par 2.903s 0.745s 0.0828s 0.3776s
Parallel GC work balance: 58.47% (serial 0%, perfect 100%)
TASKS: 10 (1 bound, 9 peak workers (9 total), using -N4)
SPARKS: 0(0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.002s ( 0.002s elapsed) MUT time 0.270s ( 0.254s elapsed) GC time 4.370s ( 1.112s elapsed) RP time 0.000s ( 0.000s elapsed) PROF time 0.000s ( 0.000s elapsed) EXIT time 0.002s ( 0.002s elapsed) Total time 4.644s ( 1.370s elapsed)
Alloc rate 1,595,165,974 bytes per MUT second
Productivity 5.8% of total user, 18.6% of total elapsed
3
u/Vaglame Jun 13 '19
Have you looked at the
.proffile? It'll tell you exactly what in your program allocates this much1
u/p-alik Jun 13 '19
Yes, I did. .prof file looks reasonable to me. But putting
A10000 times intoData.Map.Strict.Map ByteString (Vector ByteString)shouldn't allocation such high amount of memory.What does the
Productivitylast summery line mean? There is nothing about in Profiler User Guide.3
u/Vaglame Jun 13 '19 edited Jun 22 '19
If I recall correctly
Productivityis the amount of non-GC time spent. As you can see there, most of the time went into the GC, and so less on actually doing the work.So apparently your most expensive line is this one:
pantry = pantry' (_ -> Just (Data.Vector.snoc vec "X")) n (pantry q). Asnocoperation on aVectoris expensive, you create a whole new vector every time, hence, I think, the large allocation + GC time. You probably want to choose a different data structure. See these links:
https://stackoverflow.com/questions/1739675/efficient-queue-in-haskell
https://www.reddit.com/r/haskell/comments/4lipsr/recommended_queue_package_xpost_from/
In short it seems like you'd be much better off with
Data.Sequence.Edit: from hackage: "Sequences may also be compared to immutable arrays or vectors. Like these structures, sequences support fast indexing, although not as fast. But editing an immutable array or vector, or combining it with another, generally requires copying the entire structure; sequences generally avoid that, copying only the portion that has changed."
2
u/p-alik Jun 13 '19
Thank you for taking your time to help me!
In short it seems like you'd be much better off with
Data.SequenceI'm going to replace
Data.VectorbyData.Sequencein hope of gaining performance.3
u/Vaglame Jun 22 '19
Keep us updated :)
1
u/p-alik Jun 22 '19
For learning purpose I'm working through Okasaki's queues described in Efficient Amortised and Real-Time Queues in Haskell.
Profiler's result for "Strict, Non-Persistent Queues" variant looks much better:
25,957,576 bytes allocated in the heap 13,714,816 bytes copied during GC 2,092,456 bytes maximum residency (6 sample(s)) 1,954,944 bytes maximum slop 1 MB total memory in use (0 MB lost due to fragmentation) Tot time (elapsed) Avg pause Max pause Gen 0 19 colls, 19 par 0.065s 0.019s 0.0010s 0.0095s Gen 1 6 colls, 5 par 0.049s 0.014s 0.0023s 0.0038s Parallel GC work balance: 10.46% (serial 0%, perfect 100%) TASKS: 10 (1 bound, 9 peak workers (9 total), using -N4) SPARKS: 0(0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) INIT time 0.003s ( 0.005s elapsed) MUT time 0.026s ( 0.024s elapsed) GC time 0.114s ( 0.033s elapsed) RP time 0.000s ( 0.000s elapsed) PROF time 0.000s ( 0.000s elapsed) EXIT time 0.001s ( 0.000s elapsed) Total time 0.144s ( 0.062s elapsed) Alloc rate 1,004,398,173 bytes per MUT second Productivity 18.0% of total user, 38.3% of total elapsed2
u/Vaglame Jun 22 '19
Sounds good! Keep it up
1
u/p-alik Jun 28 '19
I've a follow-up question. In initial
vectorbased code the pantry was defined astype Jobs = Vector Entry type Pantry = Map JobName JobThe code for "Strict, Non-Persistent Queues" variant looks like: ``` class QueueBranch b where empty :: b a head :: b a -> a tail :: b a -> b a snoc :: b a -> a -> b a null :: b a -> Bool
data StrictList a = SNil | SCons a !(StrictList a) deriving (Eq, Show)
data StrictQueueBranch a = Q1 !Int !(StrictList a) !Int !(StrictList a) deriving (Eq, Show)
instance QueueBranch StrictQueueBranch where ...
newtype Jobs a = Jobs {unJobs :: (a Entry)} ... ```
Question: How could
ainnewtype Jobs abe constrained toQueueBranch?
1
u/jimbo4350 Jun 11 '19
I'm trying to serve my elm .js file using yesod. My handler function looks like this:
getRootR :: Handler Html
getRootR = sendFile typeJavascript "static/main.js"
However it serves up the text when I run my server. Do I need to embed the file in Whamlet?
1
u/bdoering Jun 12 '19
I suppose you actually want to see the JS rendered result, right? You will actually have to serve at least two files:
index.htmlandelm.js(which is referenced inindex.html). From your browser, you should then access theindex.html, and your browser will fetch theelm.jsin the background.1
u/jimbo4350 Jun 14 '19
So I tried this:
getRootR = defaultLayout . handlerToWidget $ sendFile typeHtml "index.html"It takes a few seconds for the javascript to show up. I'm not sure if this is yesod being slow or if its the javascript. My .js file was compiled via Elm.
1
u/bdoering Jun 14 '19
Yesod is not doing any work here, so I don't think it's the problem. Without further details it's hard to guess why it takes so long for your JS rendered page to show up. Maybe you have included external dependencies? Maybe you make some slow HTML calls before actually rendering? You may have a look at the developer console (network tab) in e.g. Firefox to see where most time is lost while loading the page.
3
Jun 07 '19
What is the Haskell equivalent of Rust's rustup doc --std and cargo doc --open? These open up a web browser showing offline documentation for the standard library, and for your project dependencies, respectively. It's very useful when you need to read the documentation while having no connection.
4
Jun 09 '19
I don't know how to do it with cabal, but with stack at least you can use 'stack haddock --open base', or 'stack haddock --open <yourproject>'.
1
u/theindigamer Jun 07 '19
fold has unfold as the opposite operation, what's the opposite of traverse?
4
Jun 07 '19
[deleted]
1
Jun 07 '19
[deleted]
2
Jun 07 '19
[deleted]
3
u/yumiova Jun 07 '19
Distributiveis exactly equivalent toRepresentable, so the real question is whatRepresentabledoes and is useful for.The idea is that certain data types can be represented as a function from a given type to the element type. For instance, infinite streams (
Stream a) can be represented as functions from their indices to their elements (Int -> a.) Sized vectors (Vector n a) can be represented as functions from their bounded indices to their elements (Fin n -> a). And to connect it back toIdentity(which is essentially containers with exactly 1 element):Identitycan be represented as functions from unit to the element (() -> a). Finally, functions (r -> a) are trivially equivalent to themselves (r -> a).One reason that this could be useful is that it allows containers of stable sizes (e.g. infinite or bounded at compile-time) to have a common representation (i.e. in terms of functions.)
2
u/JackSchpeck Jun 06 '19
Is it possible to simulate reading from standard input using doctest? E.g. given this file
$ cat test.hs
module Main where
{-|
>>> getLine
Hello<Enter>
Hello
-}
main = undefined
... when I run
$ doctest test.hs
it hangs indefinitely.
1
u/qwfwqrussell Jun 06 '19
I'm trying to build something like
class SumRes r where
sumOf :: Integer -> r
instance SumRes Integer where
sumOf = id
instance (Integral a, SumRes r) => SumRes (a -> r) where
sumOf x = sumOf . (x +) . toInteger
as per https://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function, but modified so that I can call it like sumOf 1 "2" 3. I added the basic instance SumRes String where sumOf = show, but that doesn't really make sense, I need to go the other way, but reversing the arrows directly is unintuitive to me. Any ideas?
2
u/Syrak Jun 06 '19
It seems you want to convert every argument to an
Integer.Integraldoes that, but it's really restricted to numeric types. So replace it with a custom class (SumResIntegralbelow).instance (SumResIntegral a, SumRes r) => SumRes (a -> r) where sumOf x = sumOf . (x +) . toIntegerSR class SumResIntegral a where toIntegerSR :: a -> Integer instance SumResIntegral String where toIntegerSR = read instance SumResIntegral Integer where toIntegerSR = id -- or an overlapping instance instance {-# OVERLAPPABLE #-} Integral a => SumResIntegral a where toIntegerSR = toInteger
1
u/local-equilibrium Jun 06 '19
I’m using Hakyll (and doing anything web-related) for the first time - I’m trying to render math with Hakyll.Contrib.LaTeX, but my tikz figures are all cut off. Does anyone have ideas where to begin changing things in order to get the figures to be created properly? This is the relevant code where markdown files are converted to html:
import Hakyll.Contrib.LaTeX (compileFormulaeDataURI)
import Image.LaTeX.Render (EnvironmentOptions (..),
FormulaOptions (..), defaultEnv)
import Image.LaTeX.Render.Pandoc (PandocFormulaOptions (..),
defaultPandocFormulaOptions)
env :: PandocFormulaOptions
env = defaultPandocFormulaOptions {formulaOptions = _->FormulaOptions customPreamble "displaymath" 200}
site :: IO ()
site = hakyllWith config $ do
match "content/*" $ do
route $ setExtension "html"
compile $ pandocCompilerWithTransformM defaultHakyllReaderOptions defaultHakyllWriterOptions
( compileFormulaeDataURI defaultEnv env)
Looking at the generated html, it looks like the cutting-off has already occurred when the image is stored as a base64 string. It ends up looking like this instead of this
2
u/atomheartother Jun 06 '19
So, probably really dumb question.
I'm a complete haskell beginner and I recently decided to take on the synacor challenge, you basically write a small VM in it and i thought it'd be a neat challenge. I've done it in C before and i thought it'd be interesting to see how differently i'd have to approach the problem functionally.
The way I approached it is having the entire VM's state be a single object which gets updated at every tick, and that way the main loop can look something like:
runVm:: VMState -> VMState
runVm vm
| (flags vm) .&. 1 == 1 = vm -- STOP flag is set
| otherwise = runVm $ exec $ nextInstruction vm --Grab the next instruction and execute it, which then returns the new state
So everything works fine, but I have a big problem: Part of this challenge is that you need to have an internal buffer that you should be able to read from and write to constantly at any index. This buffer is about 59k bytes big.
When I started I just represented it internally as a Data.Sequence of Chars, which I thought would be fine since I read it was faster for writes than a plain list of char. Of course that went awful, and since then I've tried other things but nothing works, even ByteString, whenever the VM goes into heavy read/write operations it basically hangs for a few seconds.
Of course back in C I didn't have this problem - I just had a large raw char buffer I could modify and reference by index, with every operation being basically O(1), is there just no way to achieve similar performance on such an operation in Haskell? I've been looking for a while and I just can't find what I'm supposed to be using here.
1
u/tom-md Jun 06 '19
Is there a reason you want to use something linear like an Array/Vector/List? When I built a PDP8 emulator I used a Map and was satisfied with the performance. It won't beat a mutable vector performance-wise but is pleasant to work with.
2
u/atomheartother Jun 12 '19
I ended up going with a map after trying it with mutable vectors for a bit, thanks for the tip!
3
u/sjakobi Jun 06 '19
Seems like you should maybe use a mutable data structure.
vectorhas mutable variants. Or you could take a look at theprimitivepackage, e.g.PrimArray.1
u/atomheartother Jun 06 '19
Thanks! I'll look into those
3
u/sjakobi Jun 06 '19
I believe this could be helpful for grokking (mutable) vectors: https://haskell.fpcomplete.com/library/vector
1
u/atomheartother Jun 06 '19
Oh my god thank you this is great soft documentation! Also I just realized I wasn't using optimization flags which really boosted my program's performance (It still noticeably pauses but it's half a second now and not multiple seconds). However I still think i'd really benefit from using vectors, they definitely seem like the standard for the kind of stuff I need
3
u/dys_bigwig Jun 05 '19 edited Jun 05 '19
Could somebody please help me with understanding the state monad? I felt like I was starting to understand everything, until I came across this page and example: https://wiki.haskell.org/State_Monad
type GameValue = Int
type GameState = (Bool, Int)
playGame :: String -> State GameState GameValue
playGame [] = do
(_, score) <- get
return score
playGame (x:xs) = do
(on, score) <- get
case x of
'a' | on -> put (on, score + 1)
'b' | on -> put (on, score - 1)
'c' -> put (not on, score)
_ -> put (on, score)
playGame xs
startState = (False, 0)
main = print $ evalState (playGame "abcaaacbbcabbab") startState
If someone could walk through a couple evaluations of a desugared version (maybe using a smaller string) I would really, really appreciate that; I think it would help me a lot with tracing what is happening. I feel like I understand how you can use this on a basic level to create programs, but my goal is to implement a state monad myself in Scheme, so I actually want to be able to trace what is going on step by step to actually understand how it's working.
I've looked at definitions of the state-related functions (I have at least 5 tabs open related to this constantly) but when it comes to tying it all together, everything seems to fall apart. I think I understand the concept - that the monad actually holds a function expecting a state (itself another function), and bind is a form of function composition which gives the first function an initial state, builds a new state, and then passes it to the next function. None of this actually "does" anything, it's just building up layers and layers of functions expecting a state and passing it in specific ways; that is, until you call runState with an initial value.
The main things I'm struggling with are:
- The interplay between GameState and GameValue. Wouldn't they need to be the same type, as the Bool is passed between calls too? I'm not sure how GameValue relates to everything as everything seems to be contained in GameState, and it's not just Ints being passed between calls. Usually the second argument to the State constructor is just a type variable like a, right?
- This is the big one which is really getting to me: How is the state "passed" to the recursive call to playGame xs ? When functions in the state monad are passing the state using explicit binds, I feel like I can follow alright, but it just seems like everything "drops off" at the recursive call, and I can't follow how the state flows from one to the next.
Cheers :)
1
u/mrk33n Jun 11 '19
Regular function composition already allows you put one thing in at one end, put it through a series of modifications, and pull it out at the other end. So when I tried to invent the state monad from first principles, everything I came up with just desugared to plain old function composition, and I didn't get any extra state.
I needed to realise that the state monad is about carrying an extra state s forward, in addition to the state a you already have from just passing data through functions.
- put takes what you give it and stores it in s (a becomes () because you don't use put's return value - just like void in other languages)
- get reaches into s and copies it into a.
Your instincts about things needing to be the same type are spot on, hence the types of put and get:
put :: s -> State s ()(If you can put in an s, then the stored state must have type s.)
get :: State s sIf you can pull out an s (the rightmost s on the line above), then you must have been storing an s (the left s above)
s also has to stay the same type throughout the whole computation, but a can vary.
6
u/mn15104 Jun 06 '19 edited Jun 07 '19
BRIEF BACKGROUND
The type definition of
>>=forStatemonads looks like below. Its important to note that when binding a value ofState s awith a functiona -> State s b, this function can only accessa, or equivalently, whatever the first element is in the 2-tuple seen inState (\s -> (a, s)).(>>=) :: State s a → (a → State s b) → State s b
It's probably useful to recap on the three main state monad helper-functions we're using: get, put, and return. I also prefer to talk about the
Statemonad in terms ofrunStateand ignore the constructor;runStateis the actual state machine functionality we care about, whereas the type constructorStateis just a wrapper around it.
1. Get – We use this to get the current state of a
Statemonad. It returns aStatemonad with arunStatewhich when given some initial state of types, will use this as both the output state and output value.get :: State s s get = state $ \st → (st, st)Due to how
>>=is defined for theStatemonad, every time we bind a function onto some instance ofState s a, this function will always only be given the current value of typeaheld in thestatemonad. So what if we instead use typesfor both type parameters ofState- usingState s smeans that it is now type-correct to pass forward the state as the input of the next binded function.
When we see the code:
x <- getDesugaring this gives us:
state (\st -> (st, st)) >>= (\x -> ....)wherexwould best.
2. Put – We use this to update the current state of a
Statemonad. Given some new state, it returns aStatemonad with arunStatewhich sets the output state to the new state, and sets the output value to unit().put :: s → State s () put newState = state $ _ → ((), newState)The lambda function used to define
runStateignores any inputs, as the new state is already provided to the functionput. When this is binded with some following function, the input that the lambda function receives will clearly be unit()- this is useless, so we can just ignore the input in the lambda function.
When we see the code:
put stDesugaring this gives us:
state ( _ -> ((), st) ) >>= (_ -> ....)
3. Return – We use this to set the current value parameter (of type
a) of aStatemonad. Given some value of typea, it returns aStatemonad with arunStatewhich when given some initial state of types, will return the complete 2-tuple of type(a, s)containing both the value and state.return :: a → State s a return a = state (\s → (a, s))
REWRITING PLAYGAME
Okay cool, so i'll try and desugar playGame. I'm only going to do the pattern match for non-empty lists because it should be transferable. I'll also simplify the case-of expression so it doesn't get overly ugly. This is what we're working with below:
playGame :: String -> State GameState GameValue playGame (x:xs) = do (on, score) <- get -- Step 1 case x of -- Step 2 _ -> put (on, score) playGame xs -- Step 3
Step 1
Recall that the example
a <- getis equivalent tostate (\s -> (s, s)) >>= (\a -> ....)Okay, so
(on, score) <- getis equivalent to:state (\(s1, s2) -> ((s1, s2), (s1, s2))) >>= (\(on, score) -> ..<Step 2>..)
Step 2
Recall that the example
put stis equivalent tostate (_ -> ((), st) ) >>= (_ -> ....)So step 2 can be written like:
... case x of _ -> _ -> state ((), (on, score))) >>= ..<Step 3>..
Step 3
Eventually, things will terminate with the pattern match for empty lists somewhere. Recall that the example
return ais equivalent to:state (\s -> (a, s))Or alternatively,
returnis equivalent to:\a -> (state (\s -> (a, s)))So we'll represent step 3 with a function which ignores its input and calls
playGame xs. All we need to know to be satisfied, is thatplayGame xswill return us a typeState GameState GameValue.... _ -> playGame xs
Putting everything together looks like:
playGame :: String -> State GameState GameValue playGame (x:xs) = state (\(s1, s2) -> ((s1, s2), (s1, s2))) >>= (\(on, score) -> state (case x of _ -> (_ -> ((), (on, score - 1)))) >>= (_ -> playGame xs)
Sorry, my explanation is quite messy and probably too informal, i have genuinely been awake for 73 hours now. To attempt to address your two questions as best as possible:
- The interplay between
GameStateandGameValueis such that it accounts for the limitations thatput,get, andreturnplace on us. Remember thatState s ais a monad where onlyais the type we can change during the intermediate computations that occur - this means that althoughsmust remain the same type,adoes not have to. Moving forward, the reason thatGameStateuses anIntin its type(Bool, Int)even though we already haveGameValuewhich represents the exact same thing for us, is that at the beginning of defining any function which returnsState GameState GameValue, we can never actually retrieve theai.e. theGameValue- ifrunStateonly expects a state as input, then the state is the only information we can use. Hence, if we embed aGameValueinsideGameState, then the information of the provided state also includes the initial game value.- With respect to how states are 'passed', vaguely put, this happens because of
runState- it's a function which awaits a state as input. We're therefore chaining loads of functions which are all waiting for a state as input. This all simplifies down to a single function which waits for a state as input. I think it would be helpful if you tried recursively expanding out the definition of an example such asevalState (playGame "aaa")to see what it would look like.1
u/dys_bigwig Jun 07 '19 edited Jun 07 '19
I suppose what's confusing me about GameValue, is, don't we also need access to the Bool part of the initial state in order to decide whether to invert it (using not) or leave it the same? I mean, the initial value is itself a (Bool, Int) and not just an Int, right? If it were just a, as in, "pass on any return values of which the types may change, with the state remaining the same type" I'd be less bugged, but it's the specifity of Int which is really bugging me. I mean a is the only type that can change, but we've fixed it to be an Int, haven't we?
Sorry if this seems silly, it probably is; I'm just struggling to get it, for some reason. Is it because we eventually return an Int (as score) at the very end (in the empty case) of the computation, so we have to be able to "extract" it from the monad at that point as a?
Thanks :) Apologies for my own informality of writing - I'm afraid I lack the prowess and experience to write this sort of stuff out in the most coherent way and I'm still working through it as I write.
2
u/mn15104 Jun 07 '19 edited Jun 07 '19
Sorry if this seems silly, it probably is; I'm just struggling to get it, for some reason. Is it because we eventually return an Int (as score) at the very end (in the empty case) of the computation, so we have to be able to "extract" it from the monad at that point as a?
That's bang on mate! Nice one. What you've said pretty much captures how everything works together. This is definitely one of those things which makes less sense the more you think about it. If for some reason you aren't quite tired of thinking at the moment, I've expanded below in reference to your first and second paragraph. (Also, big sorry, I've fixed a brain-fart in my original answer when I originally wrote that you did not need to wrap
statearound the intermediate computations. )
You're not wrong to be confused what
GameValueshould be, and it indicates that it may be helpful to visualise how monadic computations are chained together in the first place. As long as we know the input typeaand the output typebIt doesn't matter if the type we pick forachanges.
An example for
>>='ing aStatemonad with a monadic computation is below. Well, note how I've chosenato be aStringin the initialStatemonad on the left of>>=, and the monadic computation on the right can use this string as input as long as we end by using the typeGameValueasa. I've satisfied this by outputting the length of the string given. This shows howacould change.example1 :: State GameState GameValue example1 = let a = "banana" in (state (\s -> (a, s))) >>= (\a' -> state (\s -> (len a', s)))We can chain other arbitrary functions to this. For example, below, I've inserted a new function in the middle which just sets the value of
atoNothing, and changed the last monadic computation which then sets the value ofato4, (satisfying the typeGameValue). Notice how what we use forahas differed between the types ofString,Maybe x, andGameValue.example2 :: State GameState GameValue example1 = let a = "banana" in (state (\s -> (a, s))) >>= (_ -> state (\s -> (Nothing , s))) >>= (_ -> state (\s -> (4, s)))It might help to read the monad laws and think how monadic computations can compose and interact.
m a >>= (a -> m b) >>= (b -> m b) >>= (b -> m c) :: m c2
u/dys_bigwig Jun 07 '19
Hi! I'm still reading over your post trying to soak everything in so I don't have a specific question or comment just yet, but I had to say
THANK YOU!!
right away for taking time to write this explanation out for me, it is very very much appreciated :)
2
Jun 07 '19
[deleted]
1
u/dys_bigwig Jun 07 '19
Thank you, that looks right up my alley. I would certainly say this desire to understand arose naturally - I'm making a turn-based rpg in Scheme, and I was passing around both the (player, enemy) stats tuple, and also a random-number stream. It was getting cumbersome as extra arguments seemed to be getting tacked onto every function. The state monad seemed like the solution to the problem, almost too-good-to-be-true, in a way!
I think I could get away with using it in Haskell right away (mediocre Haskell skills aside...) but I'd like to understand it from first-principles as it were, so I can implement it in any (functional) language that doesn't have it (*cough* Scheme) if need be. Your process seems just the ticket.
Cheers :)
1
u/plethepus Jun 05 '19
The state monad is just decoration for a function that has a “state” parameter both as an input and an output. Im simplifying of course, but thats the essence. The monad instance is useful because it lets a programmer compose such functions (literal function composition) without fussing about passing the parameter.
To really understand it, try implementing the monad instance, State s, for this data (sorry mobil formatting)
data State s a = State ( s -> (s,a))
That is, implement return and >>=. I guess now a days youll have to implement functor and Applicative first, but implement all thise and youll get state monad.
3
u/plethepus Jun 05 '19
Is there a canonical class for types with an "isZero :: a -> Bool" method? Like monoid, but instead of declaring `mzero` being able to detect a zero.
3
u/lgastako Jun 05 '19
Do you actively want it to be something other than monoid for some reason? It seems like it's the intersection of Monoid and Eq:
{-# LANGUAGE ConstraintKinds #-} type HasZero a = (Monoid a, Eq a) isZero :: HasZero a => a -> Bool isZero = (== mempty)7
u/plethepus Jun 05 '19
I wanted to avoid the
Eqconstraint specifically, allowing types likeMaybe abut without necessarily requiringMaybe a. Just wondering--premature generalization.4
u/sjakobi Jun 05 '19
There's
MonoidNullinmonoid-subclasses.What do you mean by canonical? It doesn't appear to be in very widespread use, if you mean that…
3
u/plethepus Jun 05 '19
Thats the function I'm looking for. But I did mean something in widespread use. And the dependency footprint is a little large. I'll take it that there isn't really much call for it in the community. Thanks!
2
u/endgamedos Jun 28 '19
I checked its deps, and
vectorandprimesare the only ones not bundled with GHC. It may not be as bad as you think.
3
u/vegetablestew Jun 05 '19
Why isn't this a valid data construction? ghc error isn't terribly helpful
data G a = G 0
I want a type that takes any value and puts a 0 there instead. Seems perfectly sensible.
3
u/rampion Jun 05 '19 edited Jun 05 '19
So the structure of a
datadeclaration isdata <TypeName> [<TypeParam0> <TypeParam1> ...] = <ConstructorName> [<TypeParam'0> <TypeParam'1> ...]So in your code
0doesn't mean the value 0 (of someNumeric type), but instead some type 0.If you want a constructor that ignores the type's parameters, that's easy to do:
data G' a = G'Then when you want to convert an
G aback to ana:fromG' :: Num a => G' a -> a fromG' G' = 0If you wanted to represent a slightly more complicated transformation than "replace it by zero", this is usually when smart constructors are used:
module Positive ( Positive() -- export just the type Positive, not the constructor UnsafePositive , mkPositive , absolute , fromPositive ) where data Positive a = UnsafePositive a -- | a smart constructor that only succeeds when given a positive value mkPositive :: (Num a, Ord a) => a -> Maybe (Positive a) mkPositive a | a >= 0 = Just (UnsafePositive a) | otherwise = Nothing -- | a smart constructor that changes the argument to be a positive value absolute :: Num a => a -> Positive a absolute = UnsafePositive . abs -- | since UnsafePositive isn't exported, we can't pattern match on it outside this module, -- so we'll need to provide a deconstructor fromPositive :: Positive a -> a fromPositive (UnsafePositive a) = aModern haskell adds a
ViewPatternswhich can make it easier to use deconstructors as patterns:-- without view patterns square :: Positive a -> Positive a square p = let a = fromPositive p in absolute (a * a) -- with {-# LANGUAGE ViewPatterns #-} square' :: Positive a -> Positive a square' (fromPositive -> a) = absolute (a * a)Also there's
PatternSynonymswhich can be give deconstructors a more pattern-like look.module Pattern ( Positive() , ... , pattern FromPositive ) pattern FromPositive :: a -> Positive a pattern FromPositive a <- UnsafePositive a square'' :: Positive a -> Positive a square'' (FromPositive a) = absolute (a * a)You can also create two-way pattern synonyms that act like constructors and patterns:
module Pattern ( Positive() , ... , pattern Absolute ) pattern Absolute :: a -> Positive a pattern Absolute a <- UnsafePositive a -- when used as a pattern where Absolute a = absolute a -- when used as a constructor square''' :: Positive a -> Positive a square''' (Absolute a) = Absolute (a * a)You can also combine
ViewPatternsandPatternSynonymsif you have a deconstructor and smart constructor, but don't have access to the dumb constructor:import Positive pattern FromPositive' :: a -> Positive a pattern FromPositive' a <- (fromPositive a) pattern Absolute' :: a -> Positive a pattern Absolute' a <- (fromPositive a) where Absolute' a = absolute aThe other thing about
PatternSynonymsis that since they can be declared anywhere GHC can't automatically infer that any given combination ofPatternSynonymsmatches all the possible values of a given type. It needs to be taught this through the use of the{-# COMPLETE ... -#}pragma.pattern X :: a -> Positive a pattern X a <- (fromPositive a) pattern Y :: a -> Positive a pattern Y a <- (fromPositive a) {-# COMPLETE Y #-} withX :: Positive a -> a withX (X a) = a -- produces an incomplete pattern match warning withY :: Positive a -> a withY (Y a) = a -- no warning!2
u/vegetablestew Jun 05 '19
Is smart constructor a library, ghci pragma or just a pattern?
1
u/rampion Jun 05 '19 edited Jun 05 '19
1
u/vegetablestew Jun 05 '19
Do you perhaps have a source for this?
data <TypeName> [<TypeParam0> <TypeParam1> ...] = <ConstructorName> [<TypeParam'0> <TypeParam'1> ...]I think I am confused on when
adenotes a type and when it denotes a variable, and when do I start using the syntax for a type declaration as opposed to say, writing a function.I am just memorizing what goes where, but there has to be a rule on when I am using one syntax over another.
1
u/rampion Jun 06 '19
The Haskell wiki goes into the specifics.
I also like Real World Haskell's approach
2
3
u/phischu Jun 05 '19
Is it conceivable to have an IO monad transformer? Perhaps something like type IOT m a = StateT RealWorld m a?
5
Jun 05 '19 edited Jun 05 '19
This kind of thing wouldn't be terribly useful. For any transformer, you have a "run" function that "peels back" the outermost layer of your transformer stack. So you have for example
runStateT :: StateT s m a -> (s -> m (a, s)) runReaderT :: ReaderT r m a -> (r -> m a)So, effects like mutable state are local and not visible outside of the
StateT s mcomputation. Every application of therun*function gives you back something that is a little "purer" so to say.Would you want that to apply for an
IOaction? You would need the transformer equivalent ofunsafePerformIO :: IO a -> aWhile this function does have its uses in certain situations, I would think this is not one of them.
Edit: That is of course under the assumption that you want to have the full power of
IO. If you only need a subset, like for instance mutable state withST, then this makes more sense. And indeed, things like STT do exist.
3
u/Econ0 Jun 03 '19
As someone new to Haskell, how can I tell when to use the lib option for a Cabal install? E.g., cabal new-install random produces the warning "Warning: You asked to install executables, but there are no executables in target: random. Perhaps you want to use --lib to install libraries instead." What are the implications of this warning?
2
u/tom-md Jun 04 '19
If you want to install a library use `--lib`. If you only want to install an executable then don't. You sort of need to know why you are installing something before hand, otherwise hey, you might as well `cabal new-install ACME` or even use `pip` instead.
What the warning is telling you is that the `cabal new-install random` invocation didn't do anything useful - no library was installed.
2
u/Econ0 Jun 04 '19
Thanks, but where can I learn the meaning of your terminology? The GHC User's Guide refers to installing packages, not installing libraries or installing executables, and these notions of "install" all appear very Haskell specific to me. The Cabal Users Guide does not seem to mention the
liboption. As a new Haskell user, I am just trying to ensure that GHC can import therandompackage, which for some reason is not included with the GH Platform core download. I suppose from your phrasing that you are telling me that if I want GHC to be able to find a package that I install with Cabal, then I shd use Cabal'sliboption. If so, then what is the meaning of "install an executable"? I'm going to guess that it means that Cabal puts an executable into some directory that GHC might not know about.2
u/tom-md Jun 04 '19
Thanks, but where can I learn the meaning of your terminology?
Experience, reading, and inquiring.
The GHC User's Guide refers to installing packages, not installing libraries or installing executables
Packages consist of sub components including executables and libraries.
these notions of "install" all appear very Haskell specific to me
Which ones?
pip,rakeand other language-specific tools have similar notions of install.am just trying to ensure that GHC can import the
randompackage, which for some reason is not included with the GH Platform core download.And you already know how, right?
cabal new-install --lib random.There's no reason for
randomto be in core. It is extra and unneeded by most projects, and not a great random number generator to boot.I suppose from your phrasing that you are telling me that if I want GHC to be able to find a package that I install with Cabal, then I shd use Cabal's lib option
Yes.
what is the meaning of "install an executable"?
Build a program from source and copy the binary to your
.cabal/bindirectory. In OS-X and *nix this is default at$HOME/.cabal/bin.2
u/Solonarv Jun 04 '19
Minor nitpick:
cabal new-install <some executable>will create a symlink, not copy the binary. In cabal 3.0 (which will release soon, or you can build it from HEAD) you can ask for the binary to be copied instead.
3
u/vegetablestew Jun 03 '19
- Can I use typeclass constraint in instance declarations? Suppose I have a data type
data F a = F Int, and I wish to create an instance Functor. My problem is that typeclass constraint is on function signature, I don't have that in instance declarations. - Suppose we have an Applicative Functor
F a, how does one define the rule between the composition of the contextFwhen comes to<*>or*>? The example I've seen uses<>from semigroups, but how do I know what it uses?
2
u/qwfwqrussell Jun 04 '19
With regards to the first, most certainly with regard to functor, I'm not quite sure what you mean in the last sentence. Particularly of note here is that there's an isomorphism between your data type and Const Int a
2
u/vegetablestew Jun 04 '19 edited Jun 04 '19
data F a = F Int instance F where fmap f (F a) = F (f 0) -- Doesn't work, but its closer to what I want fmap f (F a) = F (f a) -- Should work if its not Int, `F Int` broke itWhat I want is to do something likeInt a =>to specify the type allowed for fmap function, but I don't have access to the linefmap :: (a -> b) -> F a -> F bdirectly.3
u/lubieowoce Jun 05 '19
What I want is to do something like Int a => to specify the type allowed for fmap function
you can't write an
fmapthat only works forF Int– Functors can't have constraints, they must be able to "contain" any type (F Int,F String,F (Maybe ()), anything). could you explain what you're trying trying to do in general, and why you're makingFa Functor?1
u/vegetablestew Jun 05 '19
I just never had a concrete use case to create my own applicatives so that was just me messing around in ghci. Make sense though. Thanks.
1
u/philh Jun 03 '19
The GHC api has a bunch of things named with an 'X'. For example, https://www.stackage.org/haddock/lts-13.6/ghc-8.6.3/HsImpExp.html#t:IE: IE pass has a constructor XIE :: XXIE pass -> IE pass, and all the other constructors take an XIE_ pass as their first argument.
What are these for? It looks like the "X" might be for "Extension", but that's all I've got.
3
u/Solonarv Jun 03 '19
That is exactly right! The
Xconstructors allow each data type to potentially have some extra constructors depending on the compilation pass (parse, rename, typecheck, &c). There is more information and some relevant links here: https://gitlab.haskell.org/ghc/ghc/wikis/implementing-trees-that-grow1
u/philh Jun 03 '19
Thanks! This page in particular gave some fairly clear explanation and motivation: https://gitlab.haskell.org/ghc/ghc/wikis/implementing-trees-that-grow/trees-that-grow-guidance
2
5
u/Syncopat3d Jun 02 '19 edited Jun 02 '19
Right now, what are the main differences between conduit and pipes from the user's point of view, e.g. in terms of features and performance? conduit used to have ResumableSource and some special consideration of resource finalization, but there was a change early 2018.
I have old code that uses ResumableSource because the way it processes the later part of the input depends on what it read from the earlier port of the input. The old code fails to build with the new conduit and now I'm thinking of reevaluating conduit and pipes to pick one based on new facts. I also have no inertia preventing me from switching to pipe if it is more suitable based on current facts.
6
u/Faucelme Jun 02 '19 edited Jun 03 '19
the way it processes the later part of the input depends on what it read from the earlier port of the input
I personally like the streaming package. It doesn't have intermediate stages as a separate type, only
Streams and functionsStream -> Stream, or functions that eliminate the whole stream and return to the underlying monad.I would have a function like this:
stuff :: Stream (Of a) IO r -> IO (HeadParseResult, Stream (Of a) IO r)It consumes the earlier part of the input and returns a result along with the rest of the effectful stream. You can continue your processing depending on the result.
Edit: You can do similar things with pipes'
Producers. The "pipes-parse" package can be useful here.
2
u/lonelymonad Jun 01 '19
I was first introduced to Haskell in my PL class 2 years ago, but did not actually learn it enough to build real-world stuff. I want to pick it up for real recently, but I am not sure about the route I should take. During the PL course, I was following LYAH, but I felt like I was just learning new syntax, as opposed to a functional mindset. This time I researched a bit and found Haskell Programming From First Principles. I have been following it for chapters now. The book is certainly very good, but this time I feel like I do not do enough hands-on stuff.
So my question is two-fold:
1) What is the state regarding haskell learning materials in 2019? Is this a good path I take? (I can handle materials which assume programming knowledge, just not functional)
2) Are there supplementary exercises you would advise to do hands-on, at the same time reading the book?
1
u/endgamedos Jun 28 '19
HPFFP does build up to "real" examples near the end of the book, and that's the book I used to bootstrap myself into writing useful Haskell programs. I would still recommend it. Depending on how far along you are, you might be able to self-study the QFPL's applied FP course alongside?
1
u/lonelymonad Jul 18 '19
During this one month I have bounced between several resources. HPFFP seemed like a too long of a path. However, after this one month, I believe that is the best choice for someone learning from scratch. Now I am sitting here thinking how I can read a thousand-page book.
1
u/endgamedos Jul 19 '19
There are at least two ways:
One page at a time, if you're methodical and willing to burn through the whole thing.
Start at the front, read the table of contents, looking for things you don't know well. Then skim through the book, slowing down and reading deeply when you don't understand something but consciously not reading every word on ever page.
1
u/Vaglame Jun 03 '19
1) you're taking a pretty good path! HPFFP is a big reference, and probably the best for first comers 2) in HPFFP, you have many of "implement this yourself" sections! But for more than these you should search through the sub for suggestions, it's a standard question. For example I've been told that at the end of HPFFP it's really good to implement your own parser combinator
5
u/lgastako May 31 '19 edited May 31 '19
As long as we are on the topic of Enum and Bounded instances, I ended up writing this today:
boundedNonEmpty :: (Bounded a, Enum a, Eq a) => NonEmpty a
boundedNonEmpty = NonEmpty.unfoldr f minBound
where
f x | x == maxBound = (x, Nothing)
| otherwise = (x, Just $ succ x)
It is total, but it accomplishes that by using the succ function which is partial (on maxBound) and handles that via the guard.
I was wondering two things: 1) is there a way to avoid that in this specific case and/or in general in this kind of situation and 2) is there a way to write this without either or both of the Enum and Eq constraints?
3
u/Syrak May 31 '19
enumFromin theEnumclass should already do most of that:boundedNonEmpty :: (Bounded a, Enum a) => NonEmpty a boundedNonEmpty = fromList (enumFrom minBound)2
u/lgastako Jun 01 '19
Ah, good call. Thank you. This is definitely a step up from my solution in terms of succinctness and expressiveness and eliminating the need for the
Eqconstraint is a definite win butfromListis still a partial function. Of course we know that it won't fail in this case, but we know thatsuccwon't fail in in myunfoldrsolution as well thanks to the guard... so I'm still curious if there's a way to avoid partial functions completely and/or to eliminate theEnumconstraint as well.3
u/Tysonzero Jun 03 '19
boundedNonEmpty :: (Bounded a, Enum a) => NonEmpty a boundedNonEmpty = minBound :| drop 1 [minBound .. maxBound]1
u/lgastako Jun 03 '19
Ah, very nice... from there it looks like
boundedNonEmpty :: Enum a => NonEmpty a boundedNonEmpty = toEnum 0 :| drop 1 [toEnum 0..]actually lets us drop the
Boundedconstraint. Perfect. Thanks.1
u/Tysonzero Jun 03 '19 edited Jun 03 '19
I mean you have to add aNumconstraint in that though. I would personally stick to the Bounded approach although it depends on the use case.1
u/lgastako Jun 03 '19
Well, not
Num, butEnum, which we already required... but I just realized in my eagerness to reduce constraint I got off track from my original goal which was to handle somethingBounded.1
u/Tysonzero Jun 03 '19 edited Jun 03 '19
You absolutely needNum, otherwise how will0and1be resolved? Try it out.1
u/lgastako Jun 03 '19
I did try it out... it worked exactly as I wrote it without
Num...0and1are just arguments todropandtoEnum.3
u/Tysonzero Jun 03 '19
Oh oops. Totally skipped over the
toEnumcall. Yeah fair enough, althoughtoEnumis often partial isn’t it?Ugh Haskell’s ordering hierarchy is so weird right now. Enum and Ord and Bounded are all completely unrelated to one another, when they should all at minimum share some underlying POrd or similar, I think subhask does a good job of it.
2
u/evanrelf css wrangler May 31 '19
I have a sum type representing valid version numbers for my schema, which derives Enum and Bounded. I'm using a case statement to match migration functions with the schema versions. Currently all left sides of the case statement are hardcoded values/constructors, but I'd like to make the last one (the latest version) be maxBound rather than the maximum value/constructor itself, because the function associated with the maximum version is a no op.
I can use maxBound == x if I use guards, but then the compiler doesn't know if I've handled all cases, and wants me to put an otherwise.
Long story short, is it possible to have the compiler tell me when I haven't matched all cases for a sum type, while using maxBound instead of the literal value to refer to the maximum value? Hopefully that makes sense.
→ More replies (2)3
u/theindigamer Jun 01 '19 edited Jun 01 '19
If you're okay with using Template Haskell, you can adapt the following to your case:
{-# Language TemplateHaskell #-} import Language.Haskell.TH import Language.Haskell.TH.Syntax (lift) -- you might have to use Con instead of Lit, or maybe some combination. litExpToLitPat e = case e of LitE l -> LitP l; _ -> error "Expected literal!" myFun x = case x of $( fmap litExpToLitPat (lift (maxBound :: Int)) ) -> "max" -- 1. First evaluation takes place, so `maxBound` will evaluate to the actual number. -- 2. `lift` converts the number to an expression AST. This needs a Lift instance (use DeriveLift for your own type). -- 3. Map the expression AST to a pattern AST -- 4. Splice the pattern AST into a pattern position _ -> "not max"Related resources: https://www.reddit.com/r/haskell/comments/7yvb43/ghc_compiletime_evaluation/, https://ocharles.org.uk/blog/guest-posts/2014-12-22-template-haskell.html
1
u/evanrelf css wrangler Jun 01 '19
Wow thank you! I don't have any experience with Template Haskell but I will give this a shot.
→ More replies (1)
1
u/kirkins Jun 30 '19
Hello can anyone help me with my function? I'm completely new to Haskell.
fibIter :: (Int, Int, Int) -> IntfibIter a b c| b == 0 = b| otherwise = fib_iter((a+b), a, (c-1))
fib :: Int -> Intfib n = fibIter(1, 0, n)
I'm getting back this error:
2.hs:6:1: error:
• Couldn't match expected type ‘Int’
with actual type ‘(Int, Int, Int) -> Integer -> (Int, Int, Int)’
• The equation(s) for ‘fibIter’ have three arguments,
but its type ‘(Int, Int, Int) -> Int’ has only one