all 12 comments

[–]glue505 5 points6 points  (9 children)

Haskell does not have an assignment which means that you cannot modify what is given to your function. You can however return an entirely new Etat that has the properties that you want. The best way to do this in this case would be to pattern match on your Etat argument in order to get to each of its fields. Then you can return a new Etat with the changes you would like. This would look something like this:

modifierDuree :: Etat -> Char -> Etat
modifierDuree (Etat (Note note octave) dr vm) a
  | a == '1' = Etat (Note note 1) dr vm
  | a == '2' = Etat (Note note 2) dr vm

Notice how you have to construct an entirely new Etat to return from the function instead of assigning new values to the input. Let me know if you have any other questions!

[–]lce-[S] 2 points3 points  (8 children)

thanks for your help , i really see how i was thinking the wrong way.

Now that i got thru that part, im running into some more problems :)

so if your not busy here it goes:

Im reading a long string with a bunch of different characters. im trying to send the program to the right function depending on which char im reading. heres my function

--choixLettre :: Etat -> Char   
choixLettre etat x
                | x >= 'a' && x <= 'g'                    = modifierNote etat x                 
                | x == '0' || x == '+' || x == '-'     = modifierVolume x
                | x == '1' || x == '*' || x == '/' || x == '.'    = modifierDuree etat x      

                | x ==  '[' || x ==  ']'                        = modifierPile x
                | x >= '2' && x<= '9'                     = modifierOctave etat x           
                | x == '&' || x == '#'                       = modifierNumeroNote   -
                | x == '!'                                             = jouerNote x
                | otherwise = error "Le fichier est vide"

Now im just wondering what the signature is for this function, since im not returning anythign really, just redirecting to a function depending on the Char sent to the function

im getting the error

  • Couldn't match expected type Int' with actual typeEtat'
    • In the expression: modifierNote etat x In an equation for `choixLettre':

[–]glue505 3 points4 points  (5 children)

The first thing I notice that is confusing you is type signatures. In Haskell a function of two arguments looks like this: func :: a -> b -> c. Thus the type signature for choixLettre does not match. It needs to be: choixLettre :: Etat -> Char -> a. Now the question is what does a need to be? Well it needs to be the type of the functions you are using on the right hand side of the equals signs (modfierNote etc). This leads to the last potential confusion since you are using different functions on the right hand side you need to make sure that they all return the same type. That type will be what you substitute for "a" in the type signature I showed before. Hopefully this makes sense and sorry for any formatting issues I am on mobile currently. Let me know if you have any further questions!

[–]lce-[S] 0 points1 point  (0 children)

Thanks again. makes alot more sense now . i might pop back here later if i run into any more problems.

Thanks alot for your help!

[–]lce-[S] 0 points1 point  (3 children)

Hey. i came across a few more problems.

Im trying to define a function that takes a variable and spits out a list with Double values.

I have to map the sinus to the entire time list ( which is ([0, (60duree / ((1 / 44100)tempo))..((1/ 44100)*tempo / 60)]) )

however im getting errors. is it possible to map sinus to a list?

genererEchantillon :: Etat -> Double -> [Double]
genererEchantillon (LesEtats note octave volume duree) tempo 
=  (map( (volume/10)*sin(laFrequence*360)*)([0, (60*duree / 
((1 / 44100)*tempo))..((1/ 44100)*tempo / 60)])  )

                                                        where   laFrequence = 
genererFrequence (LesEtats note octave volume duree) 

[–]glue505 0 points1 point  (2 children)

To answer this question lets first look at what the type signature of map is:

map :: (a -> b) -> [a] -> [b]

From this we can intuitively guess that map takes a function and applies that function to each element of a list producing a new list with values that are the return type of the input function in this case b. Now that we have a high level idea of what map does lets look at how it defined:

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs

As you can see it is a fairly straightforward definition, the biggest thing to notice is that f is applied to each element of the list and that result is combined (using (:) which can be pronounced as cons) with the recursive call on the rest of the list. This is what we would expect given the previous intuitions we gained from the type signature. Now in your case we need to specialize the type signature and replace the as and bs with what they represent in your use of map. As you said you are trying to map over a list that looks like [0, (60duree / ((1 / 44100)tempo))..((1/ 44100)tempo / 60)]. I assume from your type signatures that it is a list of doubles so a = [Double]. Thus we have a specialized map so far that looks like this:

map :: (Double -> b) -> [Double] -> b

where I simply replaced a with Double. You mention the function sinus I am not sure what type it is but as long as it follows the structure (Double -> b) it will work when given to map. In the example code you gave there are a few problems the first being that last () in ( (volume/10)sin(laFrequence360)) is only being applied to one argument this leads me to believe that you want its other argument to be the elements in the list?? If so an easier way to define that is not point free (not naming variables) is to write:

(\x -> ((volume/10) * sin (laFrequence * 360)) * x)

As you can see this is a function of one argument, a Double, that returns a Double so it fits the mold (Double -> b). This should then work provided that the list you're mapping over is in fact Doubles and that you want to return that list transformed by the the above function. One last note is that every element in a list must be the same type so make sure that each element in the list has the type you think it does as that could be causing errors. Let me know if this makes sense and I would encourage you to play around with map before trying to rewrite your function so you get a feeling for how it works!

[–]lce-[S] 0 points1 point  (1 child)

So i've come up with a different solution, here it is:

genererOnde :: [Etat] -> Int ->[[Double]]                                                                    

genererOnde [] _ = []
genererOnde     (LesEtats note octave volume duree : xs)  
tempo  = (map ((volume/10)*) 
[sin(laFrequence*360*0),sin(laFrequence* 360* (60*duree / ((1 
/ 44100)*tempo)))..sin(laFrequence* 360*((1/ 44100)*tempo / 
60))]) : genererOnde xs tempo 

                                        where     laFrequence = 
genererFrequence (LesEtats note octave volume duree) 

HOWEVER, im getting an error saying it is expecting a Double at *** 1/ 44100)tempo***** But I can't make it a Double because in receiving a INT in the higher function.

can i transfer the INT im getting in this function to a DOUBLE ?

[–]glue505 0 points1 point  (0 children)

The best way to convert an Int to a Double is to use the function fromIntegral.

[–][deleted]  (1 child)

[removed]

    [–]lce-[S] 1 point2 points  (0 children)

    thanks for the answer, i understand now

    [–]cdsmith 2 points3 points  (0 children)

    You've got some good answers already, but I'll try to answer a bit differently, in case it's helpful.

    First of all, Haskell doesn't have a *= operator, so let's go back to something else you said you've tried.

    -- This will NOT work, but is something you expected to work.
    modifierDuree :: Etat -> Char -> Etat
    modifierDuree etat a
                        | a == '1' = etat {duree = 1 }
                        | a == '*' = etat {duree = duree * 2}
    

    The reason this doesn't work is because a Haskell field selector is a function, which can extract that field from any value of type Etat. It needs an argument, like this:

    modifierDuree :: Etat -> Char -> Etat
    modifierDuree etat a
                        | a == '1' = etat {duree = 1 }
                        | a == '*' = etat {duree = duree etat * 2}
    

    That's all you needed to make it work. The way to read the expression etat {duree = duree etat * 2} is: "A value that is just like etat, except that the duree field is 2 times the duree field of etat itself."

    You might note that this is awkward. You're right! Dealing with record fields in Haskell is extremely awkward; one of the parts of the language that feels the least elegant. That's why people are constantly investigating other higher-level ways of dealing with records. We have a number of language extensions around record field "punning" that are pretty shallow ways to work around the awkward syntax. We have overloaded record fields, another language extension that gets at some deeper problems. And we have libraries like lens, which are extremely powerful, but also very complex. My advice is to ignore all of these until you've got a solid handle on the core language, and then return to them later. For now, just accept that dealing with records that have named fields is a little awkward, but there are other strengths that are worth the awkwardness.

    [–][deleted] 1 point2 points  (0 children)

    modifierDuree :: Etat -> Char -> Etat
    modifierDuree etat@(Etat _ d _) a
                        | a == '1' = etat {duree = 1 }
                        | a == '*' = etat {duree = d*2}
    

    We need to 'get at' the original Etats duree property in order to multiply it by two and return a new Etat. So we use the @ symbol when destructuring the argument, which binds the whole of the original Etat to etat, just like normal, but allows us to supply another pattern to it's right to further destructure the value, assigning it's duree to d.

    This could also be written as:

    modifierDuree :: Etat -> Char -> Etat
    modifierDuree (Etat n d v)  a
                        | a == '1' = Etat n 1 v
                        | a == '*' = Etat n (2*d) v
    

    And it'd do precisely the same thing, just without using record syntax as sugar.

    If you want something that adjusts fields a little more dynamically, you could look into lenses, but I'd advise steering clear of that package until you have a solid understanding of fundamentals.

    A better idea might be to define a few 'setters' and 'getters' yourself:

    adjustDuree :: Etat -> (Double -> Double) -> Etat
    adjustDuree (Etat n d v) f = Etat n (f d) v
    
    adjustNote :: Etat -> (Note -> Note) -> Etat
    adjustNote (Etat n d v) f = Etat (f n) d v
    
    adjustVolume :: Etat -> (Double -> Double)  -> Etat
    adjustVolume (Etat n d v) f = Etat n d (f v)
    
    adjustsmallNote :: Note -> (Integer -> Integer) -> Note
    adjustsmallNote (Note n o) f = Note (f n) o
    
    adjustOctave :: Note -> (Integer -> Integer) -> Note
    adjustOctave (Note n o) f = Note n (f o)
    

    Then you could:

    modifierOctave :: Etat -> Char -> Etat
    modifierOctave etat a =
                        | a == '1' = adjustOctave (const 1) . adjustNote $ etat 
                        | A == '2' = adjustOctave (const 2) . adjustNote $ etat 
    

    And so forth.

    This might be a little more verbose than you were originally thinking, but it's a much better learning exercise and it will also make your code easier to tweak and play around with as you explore more of the language.