Eli
Eli

Reputation: 2111

Haskell math type system fun

I have the following Haskell code

import Data.Int
import System.Environment

type Coord = (Int16, Int16)

distributePointsOverCircle :: Int16 -> Int16 -> [Coord]
distributePointsOverCircle points radius =
  [ (xOf point, yOf point) | point <- [1..points] ]
  where
    xOf x = abstract cos x
    yOf x = abstract sin x

    abstract :: RealFrac a => ( a -> a ) -> Int16 -> Int16
    abstract f x   = (radius *) . truncate . f . fromIntegral $ (angleIncrement * x) * truncate (pi / 180)
    angleIncrement = div 360 points

main = do
  [a,b] <- getArgs
  print $ distributePointsOverCircle (read a) (read b)

No matter what I pass to distributePointsOverCircle, it consistently gives me a list of however many Coords as I give points where each Coord's first element is the radius and second element is zero. Obviously this is not an even distribution of points.

What am I doing wrong here? Is there some type-system trickery fudging my numbers? The function I am trying to produce, written in an imperative pseudocode would be.

distributePointsOverCircle( numberOfPoints, radius )
  angleIncrement = 360 / numberOfPoints
  points         = []

  for i in 0 to (numberOfPoints -1)
    p = Point()
    p.x = (radius * cos((angleIncrement * i) * (PI / 180)))
    p.y = (radius * sin((angleIncrement * i) * (PI / 180)))

    points[i] = p

  return points

Upvotes: 3

Views: 473

Answers (2)

HaskellElephant
HaskellElephant

Reputation: 9891

Here is what I ended up with:

import Data.Int
import System.Environment

type Coord = (Int16, Int16)

distributePointsOverCircle :: Int16 -> Int16 -> [Coord]
distributePointsOverCircle points radius =
  [ (xOf point, yOf point) | point <- [1..points] ]
  where
    xOf x = abstract cos x
    yOf x = abstract sin x
    iRadius = fromIntegral radius
    angleIncrement = div 360 points
    abstract f x = round . (iRadius *) . f $ angle * (pi / 180)
      where 
        angle = fromIntegral $ angleIncrement * x

main = do
  [a,b] <- getArgs
  print $ distributePointsOverCircle (read a) (read b)

As already mentioned, the problem was that you used truncate before multiplying so that among other things truncate (pi / 180) == 0. I also think you had some errors in your main function.

Upvotes: 2

kennytm
kennytm

Reputation: 523614

It gives you a list of (r, 0) because truncate (pi / 180) == 0. Remove the truncate and the code should work fine.

abstract f x = (radius *) . truncate . f $ fromIntegral (angleIncrement * x) * (pi / 180)

Upvotes: 5

Related Questions