Cast value to Integer type

Hi everyone, what is the most elegant way to cast a variable into an integer value?

Thanks :wink:

1 Like

Can you please give an example of this? What kind of variable of what type?

Ya, sorry I was not so clear. This question is related to this post :grinning:
Maybe we can continue there because is strictly related to the OSC manipulation.
Thanks @mrreason :slightly_smiling_face:

1 Like

Ah I see :slight_smile:

But let me answer this here anyway because this is a general question I guess. And maybe it's useful for you and/or others.

You can see the type definition of a function with :t like

:t n

This will return n :: Pattern Double -> ControlPattern which means, that the n function needs to receive a Pattern from type Double.
The trick is that a 1 as parameter will automatically treat as an Double when you use it for the n function:

n 1
-- (0>1)|n: 1.0f

You can specify the type of such a number with ::Integer or ::Double:

:t 1::Integer
-- 1::Integer :: Integer

Thats why this results in an error:

n 1::Integer

And last but not least I briefly explain my mistake from the other thread cI returns a Pattern of Int

:t cI
-- cI :: Int -> String -> Pattern Int

And cF returns a pattern of type Double

:t cF
-- cF :: Double -> String -> Pattern Double

Now it should be clear why cI won't work for the function n but cF does.

2 Likes

Oh man, thank you very much for this detailed answer !! :smiling_face_with_three_hearts:

The t: maybe will save me through my long journey in learning this language.

Many thanks, really !! :slight_smile:

1 Like

Really no problem, I am glad if I could help :slight_smile:

One last thing perhaps. In case you are wondering where things like :t come from, or how the type system works. TidalCycles is an embedded domain specific language and it's underlying host language is Haskell. That's why you can do something like this:

addSix :: Num a => a -> a
addSix x = x + 6

addSix 3

This has nothing to do with Tidal but you will find that many people writing their own functions that look like this or something similar, which comes from Haskell.

1 Like

Hi,

I'm also new to Tidal and Haskell and this question caused (and causes) a little bit of headache for me too. Mixing and matching types needs some practice and getting used to in my experience.

I'm not an expert but afaik the x :: Type notation only applies to literals. It's not casting, so you can not use it for variables or functions.

You can do

a = 1 :: Double

but you can not do

b = a :: Integer

You have to use a function for that. I personally use floor or round for converting fractionals into integers, and fromIntegral for the other way around.

let
  a = 1.5
  b = floor a
  c = fromIntegral b

Now b :: Integral b => b which can be used as an Integer and c :: Num b => b which can be used as a Double. I'm not an expert, but this is how I can work around type problems right now. Haskell's type system is a little intimidating if someone is coming from the imperative world, but it's also seems pretty amazing to me.

Cheers,
Mate

P.S.: In my so very humble opinion Tidal should only use Doubles on the UI front and convert to Integers under the hood when needed, for example sample selection or scale expansion. It's not a deal breaker for me, but it can disturb the flow of musical experimentation to fight such a technical detail. Of course it's possible that I just don't see the detail that makes Integers a necessity.. If so, please let me know, I'm curious!

1 Like

That's not exactly it. You can use :: to declare the type of any value, including functions. But you can't use it to change the type of anything, or convert from one type to another. That's why b = a :: Integer fails in the above - you've already declared a to be of type Double, so can't later treat it as an Integer.

You often don't have to use :: because Haskell will infer the type of things for you, from the context.

I don't think Haskell has the concept of literals, everything is a value, including functions.

If you could share a particular example that's fiddly, we could think about how to make it easier.

1 Like

Thanks for the clarification, you're absolutely right. By 'literal' I meant a fixed value that is represented in the source code. (I searched it and in Haskell's context 'literal' means something else, something definitely beyond my pay grade.)

And here is something that might be a bit more welcoming imho.

Instead of this:

scale "major" $ floor <$> (struct "t*16" $ range 0 8 $ sine)

I would prefer this:

scale "major" $ struct "t*16" $ range 0 8 $ sine

Thanks again,
Mate

Okay so I think Tidal's current behavior is right in this example and I want to explain why:

for something like the scale function you're presumably not wanting to deal with unconstrained microtonality, so it's really only going to make sense if you convert fractional values to whole numbers anyway. but if that's your approach, then I think it should be left to the programmer to decide how the conversion is done. maybe you want to use floor, maybe you want to round, maybe you want to do something weird like check the third digit of the fractional number and if it's not 0 or 4 play middle c. Who knows!

in that case, I think it's good to rely on the actual semantic meanings of the underlying Haskell types and just go the more idiomatic Haskell solution of using fmap (or an infix operator like <$>).

I think the issue here is just better explanations of how things like fmap work so it's not a surprise to people who weren't Haskell programmers first, y'know?

Yep I'm very sympathetic to that view @clarissa but with n and note taking floating point values and supporting microtonality, why shouldn't scale? For a pentatonic scale, it could be nice to sometimes go halfway between two notes.

This is where my lack of formal musical training lets me down though - is e.g. 'halfway between two pentatonic notes' ambiguous?

But then I sense that @gadfly16 doesn't actually want that sine to produce microtonal notes. In that case, if scale accepted floating point numbers, this would work, but not do what is intended:

scale "major" $ struct "t*16" $ range 0 8 $ sine

They could put in a quantise to fix that though.

scale "major" $ struct "t*16" $ quantise 1 $ range 0 8 $ sine

As an aside I think all values are 'fixed' in Haskell in that sense.. Well there are mutable variables (you update one when you do use e.g. the d1 function) but only thanks to trickery.

Oh quantise does a round, it's an alias for qround. There's also qfloor and qceiling if you want that behaviour.

I'd be in favour of changing scale to accept floating points if this behaviour makes sense for people.. but it would be a breaking change for people who are currently having to do explicit conversion to integers.

2 Likes

Thanks, with quantise the line becomes much more elegant. I didn't know about it.

On @clarissa 's note, since double values can describe all relevant integer values, those custom roundings would be still possible to be made by the user even if scale would do an - in that case yet unnecessary - floor inside, and since scale maps integer steps of a scale to a float that means that supporting microtonal would be still possible. 0 would map to 0, 1 to 0.5, 2 to 1,... for example.

I can relate to maintaining backward compatibility, so it's a suggestion for 2.0 then. Anyway it's a general consideration that I found very helpful in my other projects to keep types at a minimum. It's good for the end user, but it's even better for function developers that they can be sure what to expect and what to give back.

Thanks again!
Mate

No there's not inherently an ambiguity to something like "halfway between the third and fourth notes of a pentatonic scale"! So, you're right, we should probably have an easy way of expressing that.

To meet both the view of "scale as indexing into a discrete set of notes" and "scale as remapping, which includes fractional values" maybe we could just have two versions of scale in Tidal? One based on the standard getScale and the other based on an alternate version like this?

getScale' :: Fractional a => [(String, [a])] -> Pattern String -> Pattern a -> Pattern a
getScale' table sp p = (\n scaleName
          -> aux (fromMaybe [0] $ lookup scaleName table) n) <$> p <* sp
          where octave s x = x `div` length s
                aux s f = noteInScale s (floor f) + (f-(floor f))*(noteInScale s $ 1 + floor f) 
                noteInScale s x = (s !!! x) + fromIntegral (12 * octave s x)

there's nothing clever here it's just interpolating between fractional notes linearly but unless I typoed that should be doing the right thing?

I sometimes want an "unscale" function - such that

forall s n : unscale s (scale s n) = n

one use case: I write a melody like <c e f g> because that's convenient - but I want to think/transform diatonically (ionian? white keys ...) e.g., add thirds/fifths that are in the scale - not chromatically add [0,4,7].

of course the problem (sort of what you are currently discussing) is what should "unscale" do if the argument is not in the scale. Perhaps drop it?

NB: the "all is float" language already exists, and is called Javascript ... I do agree that numerical conversions can become an obstacle in performance. (I don't perform but I do teach - and it's basically the same.) The "true Haskell" solution would be to use more precise types (not less). For reference, Euterpea has a polymorphic note type - restricted by the ToMusic1 class http://euterpea.com/api/euterpea-api/note-level-api/ because notes can have annotations, pitch can be absolute or relative. It could be used for scales, but I think they don't. I can't judge how practial this is, or would be for Tidal.

I don't quite have the detailed knowledge to contribute to this, but two things occur to me. Firstly, as Tidal is (mostly) used in conjuntion with SuperCollider, it might make sense to have something that leverages the rich scale/tuning module there.

The other thing to point out – maybe this is obvious – is that human pitch perception is logarithmic. So, a linear interpolation between the pitches of C and C# will not give you what a musician would expect, which is a 50 cent difference.

Yeah I get what you mean but isn't 0.5 in Tidal going to, after it goes to supercollider, give you something halfway between C (0) and C# (1)?

Here's how to do that - there's lots of treasure to be found in the superdirt hacks folder!

1 Like