Re: Trouble with record syntax and classes

Thank you all for your advice so far. I went back and tried to simplify my code, but I'm still stuck. The basic idea I want is something like an arbitrary tree structure, where MetaSines are the branches containing Sines, and Sines are the leaves containing strings. I want to recurse through the tree and pick up all the leaf strings, but only if that branch/leaf is "on" for that time. Here's my second attempt at the code: data ISine = Sine Integer Integer Integer String | MetaSine Integer Integer Integer [ISine] letter (Sine _ _ _ l) = l sub_sines (MetaSine _ _ _ xs) = xs period p _ _ _ = p offset _ o _ _ = o threshold _ _ t _ = t on :: Integer->ISine->Bool on time (p o t x) = (mod (time-(offset p o t x)) (period p o t x)) < (threshold p o t x) act time (MetaSine p o t s) |on time (p o t s) = foldr (++) (map (act time) (sub_sines p o t s)) |otherwise = [] act time (Sine p o t l) |on time p o t l = [letter p o t l] |otherwise = [] the on, period, offset, and threshold functions should work exactly the same for Sine and MetaSine, so I'd prefer to only write them once. But the act function needs to differentiate: for a Sine, it just returns a singleton list if on is true, or an empty list otherwise; but for a Metasine, it needs to get the list of all the strings in Sine objects in it's sub_sines field. It only does this if it's on. For example, let's say A / | \ B C D / | / | \ E F G H I If A, C, D, E, F and I are all on, only the strings of Sines C and I should be joined to make a to element list. If everything is on, C, E, F, G, H, I will all be joined. Anyway, the above code still doesn't work; there's a parse error for the definition of on. I don't think my arguments are right somehow. I appreciate your collective patience and expertise, and hope you can put me on the right track. Thomas

On Mon, 26 Feb 2007 23:41:14 -0600 (CST), you wrote:
Here's my second attempt at the code: ...
You've left out a bunch of constructors, and there are various other errors here and there. I think this will do what you want:
data ISine = Sine Integer Integer Integer [Char] | MetaSine Integer Integer Integer [ISine]
letter (Sine _ _ _ l) = l sub_sines (MetaSine _ _ _ xs) = xs period (Sine p _ _ _) = p period (MetaSine p _ _ _) = p offset (Sine _ o _ _) = o offset (MetaSine _ o _ _) = o threshold (Sine _ _ t _) = t threshold (MetaSine _ _ t _) = t
on :: Integer -> ISine -> Bool on time iSine = (mod (time-(offset iSine)) (period iSine)) < (threshold iSine)
act :: Integer -> ISine -> [[Char]] act time (MetaSine p o t s) = if on time (MetaSine p o t s) then foldr1 (++) (map (act time) (sub_sines (MetaSine p o t s))) else []
act time (Sine p o t l) = if on time (Sine p o t l) then [letter (Sine p o t l)] else []
But note that you have to write the equations for period, offset and threshold twice. If you want to avoid that, you can move the Sine/MetaSine discrimination into the "tail" of the data structure, something like this:
data ISineTail = SineTail [Char] | MetaTail [ISine]
data ISine = ISine Integer Integer Integer ISineTail
letter (ISine _ _ _ (SineTail l)) = l sub_sines (ISine _ _ _ (MetaTail xs)) = xs period (ISine p _ _ _) = p offset (ISine _ o _ _) = o threshold (ISine _ _ t _) = t
on :: Integer -> ISine -> Bool on time iSine = (mod (time-(offset iSine)) (period iSine)) < (threshold iSine)
act :: Integer -> ISine -> [[Char]] act time (ISine p o t (MetaTail s)) = if on time (ISine p o t (MetaTail s)) then foldr1 (++) (map (act time) (sub_sines (ISine p o t (MetaTail s)))) else []
act time (ISine p o t (SineTail l)) = if on time (ISine p o t (SineTail l)) then [letter (ISine p o t (SineTail l))] else []
Steve Schafer Fenestra Technologies Corp. http://www.fenestra.com/

Thomas Nelson wrote:
data ISine = Sine Integer Integer Integer String | MetaSine Integer Integer Integer [ISine]
Having advised you to use different field names for different record types last time, I now confuse you by saying you can share field names in the different cases inside the same type! data ISine = Sine {period, offset, threshold :: Int, letter :: String} | MetaSine {period, offset, threshold :: Int, sub_sines :: [ISine]} If the same field name is used in both cases, both fields must be of the same type, e.g., "period" is Int throughout. This only works within the same type, i.e., ISine. Other types still cannot have a field named "period". You no longer need to define a "period" function yourself, since the field "period" already does that. "period blah" works whether "blah" is a Sine or a MetaSine. "letter blah" works if "blah" is a Sine, aborts if "blah" is a MetaSine. (But you kind of want that anyway.) Dually for "sub_sines". Existing pattern-matching code still works, e.g., act time (Sine p o t l) = ... still works. (The order of p o t l follows the order of fields in the data declaraction.) You can also optionally write act time Sine{period=p, offset=o, threshold=t, letter=l} = ... Either way it is up to you. Record syntax in Haskell is a bit confusing and restrictive. Both in some sense it is also pervertedly convenient.
participants (3)
-
Albert Y. C. Lai
-
Steve Schafer
-
Thomas Nelson