-- (edit)
-- Punkt = Point
-- Streckenzug = Polygonal chain
-- distanz = distance
-- laenge = length
-- (/edit)
data Punkt = Punkt
{ x :: Float
, y :: Float
}
p1 (x1, x2) =(x1,x2)
p2 (y1, y2) =(y1,y2)
data Streckenzug = Streckenzug {
streckenzug :: [Punkt..]
}
distanz :: Punkt -> Punkt -> Float
distanz (x1,y1) (x2,y2) = sqrt (x' * x' + y' * y')
where
x'= x1-x2
y'= y1-y2
laenge :: Streckenzug -> Float
laenge (x:xs) = if length Streckenzug < 2
then Streckenzug
else sum([distanz p1 p2 | (p1,p2) <- xs ] )
Could somebody tell me why I am getting the parse error on Streckenzug?
The errors that Haskell puts out aren't really helpful.
We have the data Punkt which shall be a point in a two dimensional coordinateesystem
and the data Streckenzug which is a list of points
later on the functions distanz and laenge (length) shall give us the as the sum between adjacent points .
The errors that GHC produces are most certainly helpful. Let's go through them one by one.
Firstly, I get no parse error whatsoever, as long as I remove the spurious indentation at the start. Instead, I get this error:
main.hs:11:17: error:
Not in scope: type constructor or class ‘Punkt..’
No module named ‘Punkt’ is imported.
Looking at the relevant line, I see you've written Punkt.. for no reason. This means nothing. This should just be Punkt. here is the correction, with better indentation.
data Streckenzug = Streckenzug {
streckenzug :: [Punkt] -- Correction here
}
When I fix this, the errors change. Let's go to the next:
main.hs:15:9: error:
• Couldn't match expected type ‘Punkt’
with actual type ‘(Float, Float)’
Looking at the relevant line, I see that you're not matching on Punkt, but actually on (Float, Float)s. This is trivial to fix. Here is the solution, with better indentation:
distanz :: Punkt -> Punkt -> Float
distanz pkt1 pkt2 = sqrt (x' * x' + y' * y') -- Correction here
where
x'= x pkt1 - x pkt2
y'= y pkt1 - y pkt2 -- Note the adjustment here.
Please understand that a Punkt is not simply a tuple of floats; it's treated seperately by the compiler.
Unfortunately, I would try to correct your function laenge, but I cannot at all work out what you mean by it – the types are completely broken, and your intention is unclear. I would recommend learning from the above mistakes and trying again.
All in all, however, the following file works:
data Punkt = Punkt
{ x :: Float
, y :: Float
}
data Streckenzug = Streckenzug {
streckenzug :: [Punkt]
}
distanz :: Punkt -> Punkt -> Float
distanz pkt1 pkt2 = sqrt (x' * x' + y' * y')
where
x'= x pkt1 - x pkt2
y'= y pkt1 - y pkt2
The functions p1 and p2 are completely useless and so I've removed them; not only are they identical, but also are both of type p1, p2 :: (a,b) -> (a,b), in other words p1 = p2 = id.
Related
I have this code
type Point = (Int, Int)
data Points
= Rectangle Point Point
| Union Points Points
| Difference Points Points
inMax :: Int -> Int -> Int
inMax a b
| a < b = b
| otherwise = a
inMin :: Int -> Int -> Int
inMin a b
| a < b = a
| otherwise = b
inPoints :: Point -> Points -> Bool
inPoints (x, y) Rectangle (x1, y1) (x2, y2) = ((inMin x1 x2) <= x && x <= (inMax x1 x2)) && ((inMin y1 y2) <= y && y <= (inMax y1 y2))
I want to know if a point is in Rectangle but i got the next error: The constructor `Rectangle' should have 2 arguments, but has been given none. Why? How I should fix it?
In order to "unpack" the Rectangle, then you write Rectange between parenthesis:
inPoints :: Point -> Points -> Bool
inPoints (x, y) (Rectangle (x1, y1) (x2, y2)) = --- …
-- ↑ parenthesis ↑
We thus perform pattern matching to obtain the points of the Rectangle. You can not use Rectange itself as a patern.
The expression inPoints (x, y) Rectangle (x1, y1) (x2, y2), no matter if it appears in a pattern left of the =, or right of it, parses as
(((inPoints (x,y))
Rectangle)
(x1,y1))
(x2,y2)
i.e. Rectangle is passed as an argument all by itself, and then (x1,x2) and (x2,y2) are passed as additional, separate arguments.
That's not completely absurd: in an expression, Rectange is just a function Point -> Point -> Points, and functions can be passed as function arguments just like any other values. For example you could define something like
unionBuilt :: (Point -> Point -> Points) -> [Point] -> Points
unionBuilt f (x₀:x₁:xs) = Union (f x₀ x₁) (unionBuilt xs)
unionBuilt f ... = ...
and that could then legitimately be called like
unionBuilt Rectangle [(1,2),(3,4)]
But in a pattern match, all constructors must be fully applied, i.e. if you pattern match on Rectangle then it must always come with two patterns for the containing points (even if you're not interested in them; then you need to use a blank pattern). This generally requires wrapping it in parentheses
So what you want is
inPoints (x,y) (Rectangle (x₁,y₁) (x₂,y₂)) = ...
Like most parentheses in Haskell expressions, these are only needed if the precedence rules don't already establish this way of parsing. In particular, if you had defined your function as an infix operator, then you would have been fine, and don't actually need any parentheses at all (except for the tuples):
(∈) :: Point -> Points -> Bool
(x, y) ∈ Rectangle (x₁, y₁) (x₂, y₂)
= inMin x₁ x₂ <= x && x <= inMax x₁ x₂
&& inMin y₁ y₂ <= y && y <= inMax y₁ y₂
This is because prefix function application, including constructors like Rectangle, always binds more tightly than infix operator application.
I started this function as follows:
type Place = (String, Float, Float, [Int])
distanceList :: Float -> Float-> [Place] -> [Float]
distanceList _ _ [] = []
distanceList degN degE ((location, float1, float2, rainfall):place) =
sqrt((float1-degN)^2 + (float2-degE)^2) : distanceList degN degE place
minDistance :: Float -> Float-> Float -> Float -> [Float] -> Float
minDistance _ _ _ _ [] = 0
minDistance degN degE float1 float2 (x:xs)
| x < minDistance degN degE float1 float2 xs = x
| otherwise = minDistance degN degE float1 float2 xs
closestPlace :: Float -> Float -> [Place] -> String
closestPlace _ _ [] = " An error as occured "
closestPlace degN degE ((location, float1, float2, rainfall):place)
| rainfall !!0 == 0 && pythag == distance = location
| otherwise = closestPlace degN degE place
where
pythag = sqrt((float1-degN)^2 + (float2-degE)^2)
distance = minDistance degN degE float1 float2 (distanceList
degN degE place)
i have test data which is passed through the Place. and the two floats are coordinates north and east, i need to use Pythagoras theorem to return a single String of which location is closest.
The code above runs but when thee closestPlace function runs it never gets passed the first part which is the error message, i just need the function to compare the Pythagorus output of the two functions above to and print the string of the location which is closest to the coordinates inputted.
the code to run this is as follows:
demo :: Int -> IO ()
demo 7 = putStrLn (closestPlace 55.0 (-5.3) testData)
You're pretty much there. As far as calculating the distance goes, one observation can be made:
If the distance to point A is greater than the distance to point B, the square of distance to A will be greater than the square of the distance to B.
This means that instead of comparing distances (and computing a square root unnecessarily), we can compare squares of them, which are calculated like so:
dist2 x1 y1 x2 y2 = (x2-x1)^2 + (y2-y1)^2
However, you also want to extract some additional metadata. Two common approaches are appending the sorting key and stripping it after, or using the comparison function directly. Here, the second approach will work quite well:
closestPlace :: Float -> Float -> [Place] -> Place
closestPlace x y = minimumBy (comparing (dist2P x y))
I've added a helper here called dist2P that allows us to compare a pair of points to a place directly; this is convenient, because we can then use comparing to automatically apply that on a pair of points that is then used by minimumBy to find the closest point.
dist2P :: Float -> Float -> Place -> Float
dist2P x y (_, x', y', _) = dist x y x' y'
That helper then gets partially applied with the source point.
Thanks to minimumBy we're also able to eta-reduce closestPlace, which is always nice.
And finally, I've decided to change the signature of your function to return Place instead of String. Why? Well, if it turns out you need any other information from the place, you can easily access it with a getter like so:
placeName :: Place -> String
placeName (s, _, _, _) = s
And similarly, your original function would thus be implemented as a composition of the two:
closestPlaceName :: Float -> Float -> [Place] -> String
closestPlaceName x y = placeName . closestPlace x y
Yesterday I finally decided to start learning Haskell. I started scanning through tutorials, but quickly decided practical exercises would be more beneficial. Therefore I proceeded to port a python script of mine which supposedly simulates gravity into Haskell. To my surprise it actually worked and the generated values match those of python.
I realize the implementation is probably absolutely terrible. The horrible lack of performance does not bother me so much, but what bothers me is that I keep running out of memory when attempting to run the simulation for a longer period of time. Is this because the implementation is inherently flawed or can it be made to work?
I have tried to construct the main loop with three different approaches: "iterate", a recursive function (I read about tail recursion, but didn't manage to make it work) and with a more experimental recursive do function. The functions in question are named simulation, test and test2. I compiled the program with option "-O2".
Why does the program run out of memory, and what can I do to prevent that?
Not so relevant parts of the code:
import System.Environment
import Data.List (tails)
import System.CPUTime
import Text.Printf
import Control.Exception
gConst = 6.674e-11
data Vector = Vector2D Double Double | Vector3D Double Double Double deriving (Show)
deltaVector :: Vector -> Vector -> Vector
deltaVector (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x2 - x1) (y2 - y1)
deltaVector (Vector3D x1 y1 z1) (Vector3D x2 y2 z2) = Vector3D (x2 - x1) (y2 - y1) (z2 - z1)
data Position = Position Vector deriving (Show)
data Velocity = Velocity Vector deriving (Show)
distance2DSquared (Vector2D deltaX deltaY) = deltaX ** 2 + deltaY ** 2
distance3DSquared (Vector3D deltaX deltaY deltaZ) = (distance2DSquared $ Vector2D deltaX deltaY) + deltaZ ** 2
distance vector = sqrt (distance3DSquared $ vector)
force vector mass1 mass2 = gConst * (mass1 * mass2) / (distance3DSquared vector)
acceleration force mass = force / mass
vectorComponentDivide (Vector2D x y) c = Vector2D (x/c) (y/c)
vectorComponentDivide (Vector3D x y z) c = Vector3D (x/c) (y/c) (z/c)
vectorComponentMultiply (Vector2D x y) c = Vector2D (x*c) (y*c)
vectorComponentMultiply (Vector3D x y z) c = Vector3D (x*c) (y*c) (z*c)
vectorComponentAdd (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1+x2) (y1+y2)
vectorComponentAdd (Vector3D x1 y1 z1) (Vector3D x2 y2 z2) = Vector3D (x1+x2) (y1+y2) (z1+z2)
invertedVector (Vector2D x1 y1) = Vector2D (-x1) (-y1)
invertedVector (Vector3D x1 y1 z1) = Vector3D (-x1) (-y1) (-z1)
normalizedVector :: Vector -> Vector
normalizedVector vector = vectorComponentDivide vector $ distance vector
velocity vel0 mass1 mass2 vector deltaT =
vectorComponentMultiply (vectorComponentAdd vel0 (vectorComponentMultiply (normalizedVector vector) (acceleration (force vector mass1 mass2) mass1))) deltaT
data Actor = Actor String Vector Vector Double deriving (Show)
earth = Actor "Object1" (Vector3D 0 0 0) (Vector3D 0 0 0) 10
moon = Actor "Object2" (Vector3D 10 0 0) (Vector3D 0 0 0) 10
actors = [earth, moon]
combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [ [] ]
combinations n xs = [ y:ys | y:xs' <- tails xs
, ys <- combinations (n-1) xs']
updateVelocity [(Actor name1 position1 velocity1 mass1),(Actor name2 position2 velocity2 mass2)] =
[(Actor name1 position1 a mass1),(Actor name2 position2 b mass2)]
where a = velocity velocity1 mass1 mass2 vector deltaT
b = velocity velocity2 mass2 mass1 (invertedVector vector) deltaT
vector = deltaVector position1 position2
deltaT = 1
updatePosition [(Actor name1 position1 velocity1 mass1),(Actor name2 position2 velocity2 mass2)] =
[Actor name1 (vectorComponentAdd position1 velocity1) velocity1 mass1, Actor name2 (vectorComponentAdd position2 velocity2) velocity2 mass2]
Relevant parts:
update list = map updatePosition (map updateVelocity list)
simulation state n = do
if n == 0
then do
print state
return ()
else do
let newState = update state
simulation newState $! (n-1)
test list n = iterate update list !! n
test2 list 0 = list
test2 list n = (test2 (update list) (n-1))
time :: IO t -> IO t
time a = do
start <- getCPUTime
v <- a
end <- getCPUTime
let diff = (fromIntegral (end - start)) / (10^12)
printf "Computation time: %0.3f sec\n" (diff :: Double)
return v
main :: IO ()
main = do
let combo = combinations 2 actors
putStrLn "Hello World!"
let n = 1000000
print n
--time $ print (test combo n)
time $ simulation combo n
_ <- getLine
putStrLn "BAI"
I believe laziness harms your code: your code builds large thunks (unevaluated expressions) which lead to OOM.
For instance, iterate is (in)famous for leading to large thunks, when you access the resulting list in the middle without forcing the previous list elements. More precisely
iterate f x !! n
is bad, since it will build the expression f (f (f ...(f x))) before really performing any work. We want to evaluate every list element before accessing the next one. This could be dome by a custom !! function:
(!!!) :: [a] -> Int -> a
[] !!! _ = error "!!!: out of range"
(x:_ ) !!! 0 = x
(x:xs) !!! n = seq x (xs !!! pred n)
Now we can use iterate f a !!! n without large thunks building up.
This has the same problem:
simulation state n = do
if n == 0
then do
print state
return ()
else do
let newState = update state
simulation newState $! (n-1)
It will build large update (update (update ...)) thunks without evaluating them. A possible fix could be
...
(simulation $! newState) $! (n-1)
However, keep in mind that in your case newState is a list (of lists!). In such case, seq or $! will only demand the list to be evaluated as far as its first cell constructor -- just enough to check whether the list is empty or not. This "forcing" might be enough or not for your purposes.
There is a library function named deepSeq which will force the full list, if really needed (use Hoogle to find the docs).
Summing up: lazy evaluation has its benefits and its downsides. It usually allows for more efficiency, e.g. sometimes providing constant space list processing without the need of writing carefully crafted functions. It also allows infinite lists tricks which are handy. However, it can also cause unwanted thunks to stick around for too long, wasting memory. So, in those cases, programmers have some burden put on them. Especially when one is used to strict semantics, these issues can be scary at first (we've been there!).
Again I'm requesting comments about how a given design should be implemented in Haskell.
Thanks in advance to everyone providing helpful comments. Also I hope this could be an aid to other Haskell novices like me, having a practical sample code.
This time, we have a polymorphic function doSampling (in module Samples) that takes a generic function f and
a list of reals (indexes) and returns a Samples (indexes, values=f(indexes)). We want implement doSampling only once, as it doesn't matter if is f is a Polynomial or a Sinus. For that,
we have introduced an interface Function, and have Polynomial and Sinus types implement it.
The following is the design being implemented:
Edit 1:
There is a debate on the Function interface (class in Haskell). It has been suggested it is not actually necessary, as doSampling may take a "nude" function (Double -> Double) instead.
But, how to do it, if you need some extra state within the nude function (coeffs for a polynomial, amp+freq+phase for a sinus?
Edit 2:
Very good answers by kosmikus and by Chris Taylor. Thanks.
A key idea in both: have
doSampling :: (Double -> Double) -> [Double] -> Samples
This is: it takes a function (Double -> Double) (instead of Function) and list and returns samples.
My intention was to keep the state of Polynomials and Sinuses. That is not regarded in Chris answer, but it is in kosmikus'. On the other hand, the weak point in kosmikus version could be how to extend its Function definition if you don't have access to the source code.
I would also point out:
Chris' idea of encapsulating a polynomial or a sinus into a function (Double -> Double) by means of a factory function mkPolynomial or mkSinus that generates (using currying?) the desired function taking the apropriate parameters. (Although you can't consult the parameters later).
kosmikous' idea of using value to transform (also using currying?) a Function into a (Double -> Double)
Both answers are worth reading as they have other little Haskell tricks to reduce and simplify code.
In sum
Chris answers does not support keeping the state of a Polynomial or of a Sinus
kosmikus answers is not extensible: adding new type of functions (Cosinus ...)
my answer (being verbose) does overcome the previous downsides, and it would allow (this not necessary for the problem) impose Function types to have more associated-functions apart of value (in the sense of how an java-interfaces work).
My own approach
main (usage)
import Polynomial
import Sinus
import Function
import Samples
-- ...............................................................
p1 = Polynomial [1, 0, 0.5] -- p(x) = 1 + 0.5x^2
s1 = Sinus 2 0.5 3 -- f(x) = 2 sin(0.5x + 3)
-- ...............................................................
-- sample p1 from 0 to 5
m1 = doSampling p1 [0, 0.5 .. 5]
m2 = doSampling s1 [0, 0.5 .. 5]
-- ...............................................................
-- main
-- ...............................................................
main = do
putStrLn "Hello"
print $ value p1 2
print $ value s1 (pi/2)
print $ pairs m1
print $ pairs m2
Function
module Function where
-- ...............................................................
-- "class type" : the types belonging to this family of types
-- must implement the following functions:
-- + value : takes a function and a real and returns a real
-- ...............................................................
class Function f where
value :: f -> Double -> Double
-- f is a type variable, this is:
-- f is a type of the Function "family" not an actual function
Samples
module Samples where
import Function
-- ...............................................................
-- Samples: new data type
-- This is the constructor and says it requieres
-- two list, one for the indexes (xs values) and another
-- for the values ( ys = f (xs) )
-- this constructor should not be used, instead use
-- the "factory" function: new_Samples that performs some checks
-- ...............................................................
data Samples = Samples { indexes :: [Double] , values :: [Double] }
deriving (Show)
-- ...............................................................
-- constructor: it checks lists are equal size, and indexes are sorted
new_Samples :: [Double] -> [Double] -> Samples
new_Samples ind val
| (length ind) /= (length val) = samplesVoid
| not $ isSorted ind = samplesVoid
| otherwise = Samples ind val
-- ...............................................................
-- sample a funcion
-- it takes a funcion f and a list of indexes and returns
-- a Samples calculating the values array as f(indexes)
doSampling :: (Function f) => f -> [Double] -> Samples
doSampling f ind = new_Samples ind vals
where
vals = [ value f x | x <- ind ]
-- ...............................................................
-- used as "error" in the construction
samplesVoid = Samples [] []
-- ...............................................................
size :: Samples -> Int
size samples = length (indexes samples)
-- ...............................................................
-- utility function to get a pair (index,value) out of a Samples
pairs :: Samples -> [(Double, Double)]
pairs samples = pairs' (indexes samples) (values samples)
pairs' :: [Double] -> [Double] -> [(Double, Double)]
pairs' [] [] = []
pairs' [i] [v] = [(i,v)]
pairs' (i:is) (v:vs) = (i,v) : pairs' is vs
-- ...............................................................
-- to check whether a list is sorted (<)
isSorted :: (Ord t) => [t] -> Bool
isSorted [] = True
isSorted [e] = True
isSorted (e1:(e2:tail))
| e1 < e2 = isSorted (e2:tail)
| otherwise = False
Sinus
module Sinus where
-- ...............................................................
import Function
-- ...............................................................
-- Sinus: new data type
-- This is the constructor and says it requieres
-- a three reals
-- ...............................................................
data Sinus = Sinus { amplitude :: Double, frequency :: Double, phase :: Double }
deriving (Show)
-- ...............................................................
-- we say that a Sinus is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Sinus where
value s x = (amplitude s) * sin ( (frequency s)*x + (phase s))
Polynomial
module Polynomial where
-- ...............................................................
import Function
-- ...............................................................
-- Polynomial: new data type
-- This is the constructor and says it requieres
-- a list of coefficients
-- ...............................................................
data Polynomial = Polynomial { coeffs :: [Double] }
deriving (Show)
-- ...............................................................
degree :: Polynomial -> Int
degree p = length (coeffs p) - 1
-- ...............................................................
-- we say that a Polynomial is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Polynomial where
value p x = value' (coeffs p) x 1
-- list of coeffs -> x -> pw (power of x) -> Double
value' :: [Double] -> Double -> Double -> Double
value' (c:[]) _ pw = c * pw
value' (c:cs) x pw = (c * pw) + (value' cs x x*pw)
You certainly don't need the Function class. All this heavyweight class, instance, member variable fluff is one of the things that Haskell is designed to avoid. Pure functions can be much more flexible.
Here's a simple way of doing what you want.
type Sample = ([Double], [Double])
newSample xs vs
| isSorted xs && length xs == length vs = (indices, values)
| otherwise = ([], [])
pairs = uncurry zip
doSampling :: (Double -> Double) -> [Double] -> Sample
doSampling f xs = newSample xs (map f xs)
mkPolynomial :: [Double] -> (Double -> Double)
mkPolynomial coefs x = go coefs
where
go [] = 0
go (c:cs) = c + x * go cs
mkSinus :: Double -> Double -> Double -> (Double -> Double)
mkSinus amp freq phase x = amp * sin (freq * x + phase)
p1 = mkPolynomial [1, 0, 0.5] -- 1 + 0.5x^2
s1 = mkSinus 2 0.5 3 -- 2 sin(0.5x + 3)
m1 = doSampling p1 [0, 0.5 .. 5]
m2 = doSampling s1 [0, 0.5 .. 5]
main :: IO ()
main = do
print $ p1 2
print $ s1 (pi/2)
print $ pairs m1
print $ pairs m2
[Expanded my comment on request.]
I'd probably do this roughly as follows:
import Data.Functor
-- Use a datatype rather than a class. Yes, this makes it harder to
-- add new types of functions later, and in turn easier to define new
-- operations. ("expression problem")
data Function =
Sinus { amplitude :: Double, frequency :: Double, phase :: Double }
| Polynomial { coeffs :: [Double] }
deriving (Show)
-- Interpreting a Function as an actual function.
value :: Function -> (Double -> Double)
value (Sinus amp freq ph) x = amp * sin (freq * x + ph)
value (Polynomial cs) x = value' cs x
-- Rewrite value' to not require non-empty lists. This can also be
-- nicely written as a fold.
value' :: [Double] -> Double -> Double
value' [] _ = 0
value' (c:cs) x = c + x * value' cs x
data Samples = Samples { indexes :: [Double] , values :: [Double] }
deriving (Show)
-- Use Maybe to detect error conditions, instead of strange values
-- such as voidSamples.
newSamples :: [Double] -> [Double] -> Maybe Samples
newSamples ind val
| length ind /= length val = Nothing
| not $ isSorted ind = Nothing
| otherwise = Just (Samples ind val)
doSampling :: (Double -> Double) -> [Double] -> Maybe Samples
doSampling f ind = newSamples ind (map f ind)
isSorted :: (Ord t) => [t] -> Bool
isSorted [] = True
isSorted [e] = True
isSorted (e1:e2:es)
| e1 < e2 = isSorted (e2:es)
| otherwise = False
-- This is just zip.
pairs :: Samples -> [(Double, Double)]
pairs (Samples idxs vals) = zip idxs vals
p1 = Polynomial [1, 0, 0.5] -- p(x) = 1 + 0.5x^2
s1 = Sinus 2 0.5 3 -- f(x) = 2 sin(0.5x + 3)
m1 = doSampling (value p1) [0, 0.5 .. 5]
m2 = doSampling (value s1) [0, 0.5 .. 5]
-- The <$> maps over a Maybe.
main = do
putStrLn "Hello"
print $ value p1 2
print $ value s1 (pi/2)
print $ pairs <$> m1
print $ pairs <$> m2
data Point = Point Float Float deriving (Show)
data Line = Line Point Point deriving (Show)
onLine :: Line -> Point -> Bool
onLine (Line (Point x1 y1) (Point x2 y2)) (Point x y) = True
Is there a way not to use so many brackets ?
I recommend a tool called hlint for identifying places where you can simplify your code.
In your code /as written/, you're not using the values x1, y1, x2, y2, x or y, so you could just write:
onLine _ _ = True
However, I assume that's just a stub, and in reality you will do something with the variables. In general, if you really need to reference all those variables, then you need to write it the way you have done. However, maybe you're using a helper function that only needs the entire line value. Then you could write something like:
onLine l p = blah blah blah
-- use slope l and yIntercept l to figure out if p is on the line
slope :: Line -> Float
slope (Line (Point x1 y1) (Point x2 y2)) = (y2 - y1) / (x2 - x1)
yIntercept :: Line -> Float
yIntercept (Line (Point x1 y1) (Point x2 y2)) = blah blah blah
Alternatively, you can just use accessor functions to extract the x and y co-ordinates from points and lines, but in this case it will probably make your code messier.
Also, in Haskell I believe it's generally more efficient to use Double rather than Float.
You can sometimes avoid brackets with record notation, sometimes with $, sometimes with infix functions, and sometimes they're OK if not excessive.
Let's use record notation for points, which get heavy access for the coordinates, but we'll leave Line alone.
data Point = Point {x::Double,y::Double} deriving (Show)
data Line = Line Point Point deriving (Show)
This defines x :: Point -> Double and y :: Point -> Double.
There's no such thing as equality for floating points, but I'll go for roughly right:
accuracy = 0.000000000001
is :: Double -> Double -> Bool
is x y = abs (x - y) < accuracy
I can use this as x point1 `is` x point2 neatly avoiding the bracketed is (x point1) (x point2)
When your data structure is not so heavily nested with pattern matching, a few brackets are easy to read:
gradient :: Line -> Double
gradient (Line one two) = (y two - y one) / (x two - x one)
But we can go one level deeper without using excessive brackets because of the functions x and y.
asFunction :: Line -> (Double -> Double) -- ( ) for clarity, not necessity
asFunction l#(Line point _) = \xx -> gradient l * (xx - x point) + y point
Notice I've used l# to introduce an alias for (Line point _) to save typing on the right.
Now we can use the infix function trick to get rid of a few more brackets:
onLine :: Line -> Point -> Bool
onLine l p = l `asFunction` x p `is` y p
On the right hand side, you can use $ to get rid of brackets, but you can't use it on the left in pattern matching because it's a function f $ x = f x. For example
this (that (the other thing (something other)))
= this $ that $ the other thing $ something other
= this . that . the other thing $ something other
You can take the line and point apart within the function by defining accessors, but there is no way to do the pattern matching without the parentheses.
Another way of getting rid of the parentheses is to do the pattern matching in a number of case expressions:
onLine l p = case l of
Line p1 p2 -> case p1 of
Point x1 y1 -> case p2 of
Point x2 y2 -> case p of
Point x y -> True -- you can use x1,y1,x2,y2,x and y here
This is close to what the compiler 'translates' the patternmatches to, but of course this is not much of an improvement!
However, there are a number of ways of writing this expression that also translate to the same cascaded pattern matching; here's one:
onLine l p = let
Line (Point x1 y1) (Point x2 y2) = l
Point x y = p
in True
and here's another:
onLine l p = True where
Line (Point x1 y1) (Point x2 y2) = l
Point x y = p
The last one is pretty nice and readable, in my opinion, but the other suggestions are much better, since they'll lead to a better structured program!
(There's some stuff about irrefutable patterns that I'm glossing over, and this only works for single-constructor datatypes)