After becoming interested in various bits of functional programming provided by non-functional languages, I've recently decided to start learning Haskell.  As I'm fairly experienced with conventional imperative programming languages,  I decided to make my hello world something a little more complex - an implementation of gradient descent with 2 variables.The idea with this code is that you fill in the training set for the algorithm in the code, and then run the code something similar to this:descentFunc = singleDescend trainingSet 0.1deltas = descend descentFunc ( 100, 1 ) 100000Where 0.1 is the learning rate (referred to as lr in the code) 100000 is the number of iterations for the loop, and ( 100, 1 ) is an initial guess for the coefficients.The actual code that's running is below.  As I'm entirely new to Haskell, I was wondering whether code such as this is acceptable?  And whether there's any obvious idioms I'm missing/misusing or any glaring style errors I've made.Any comments on my implementation of the algorithm are welcome also.import Data.ListtrainingSet = [ (1.0,10.0),(2.0,20.0),(3.0,30.0),(4.0,40.0) ]hypothesis (d1,d2) x = d1 + (d2 * x)squareDiff d (x,y) = diff * diff    where diff = ( hypothesis d x ) - ycostFunc ts d = scaleFactor * sum squares    where scaleFactor = 1.0 / (2 * genericLength ts)          squares = map (squareDiff d) tsdiff d (x,y) = (hypothesis d x) - y descendTheta thetaFunc deltaFunc ts lr deltas =     deltaFunc deltas - (scaleFactor * sum scaledDiffs)    where scaleFactor = lr / genericLength ts          diffs = map (diff deltas) ts          scaledDiffs = map (\(x,y) -> x * thetaFunc y) $ zip diffs tsdescendThetaZero = descendTheta (\_ -> 1) fstdescendThetaOne = descendTheta (fst) sndsingleDescend ts lr deltas = (thetaZero,thetaOne)    where thetaZero = descendThetaZero ts lr deltas          thetaOne = descendThetaOne ts lr deltasdescend func deltas i    | i == 0 = deltas    | otherwise = descend func ( func deltas ) ( i - 1 )
Simple Gradient Descent Algorithm In Haskell
haskell
Welcome to haskell, Your code is quite good for a beginner, I have only peripheral recommendations on your style.import Test.HUnitimport Data.Listimport Control.ArrowAs @epsilon '2' halbe suggested below, use type signatures to define your functions first, this would help you avoid obvious errors.type DeltaPair = (Double, Double)type PairFunc = DeltaPair -> DoubletrainingSet = [(1.0,10.0),(2.0,20.0),(3.0,30.0),(4.0,40.0)]The classes in Control.* are often very useful. These are just your functions that have been modified a little bit. This may seem like overkill to you, but as a beginner, you would profit tremendously if you internalize the functions in the Control.* space. At the very least, understand the functions used here. (I would certainly recommend your definition of the diff over this, but the point here is one of demonstration.)diff :: DeltaPair -> DeltaPair -> Doublediff d = uncurry (-) . first (flip hypothesis d)    where hypothesis x = uncurry (+) . first (* x)The scaleDiffs can be written in two ways, one by recognizing that it is actually a zipWith and the other, using the functions in Control.Arrow to manipulate the pair. The second gets you a pointfree form. Use which ever you feel comfortable with, but understand both.I have replaced genericLength with fromIntegral . length because it seems from your training set that the set is not very large. If that is not the case, change it back to genericLengthdescendTheta :: PairFunc -> PairFunc -> [DeltaPair] -> Double -> DeltaPair -> DoubledescendTheta thetaFunc deltaFunc ts lr deltas = deltaFunc deltas - sSum    where scaleFactor = lr / fromIntegral (length ts)          -- scaledDiffs ts = zipWith (\x y -> x * thetaFunc y) (diffs ts) ts          scaledDiffs ts = map (uncurry (*) . second thetaFunc) $ zip (diffs ts) ts          diffs = map (diff deltas)          sSum = scaleFactor * sum (scaledDiffs ts)It is often hard to decide which definitions go on the top level. A good rule of thumb is to see if the function being defined can be reused elsewhere. If they can't then they probably are better of as sub definitions under a where clause.singleDescend :: [DeltaPair] -> Double -> DeltaPair -> DeltaPairsingleDescend ts lr deltas = (fn descendThetaZero, fn descendThetaOne)    where fn f = f ts lr deltas          descendThetaZero = descendTheta (const 1) fst          descendThetaOne = descendTheta fst sndWe should always be on the look out for general functions. These help us tremendously in refactoring the code. Here is one such function.napply :: Int -> (c -> c) -> c -> cnapply n = ((!! n) .) . iteratedescend :: (DeltaPair -> DeltaPair) -> DeltaPair -> Int -> DeltaPairdescend = flip . flip napplydeltas n = descend descentFunc (100, 1) n  where descentFunc = singleDescend trainingSet 0.1A few unit teststests = TestList [  TestLabel 1 test1,  TestLabel 2 test2,  TestLabel 3 test3  ]test1 = TestCase $ assertEqual A1 (100.0, 1.0) (deltas 0)test2 = TestCase $ assertEqual A1 (92.25,-17.25) (deltas 1)test3 = TestCase $ assertEqual A1 (89.8375,-19.875) (deltas 2)tt = runTestTT tests These two does not seem to be used any where{-squareDiff :: Num a => (a, a) -> (a, a) -> asquareDiff = ((^ 2) .) . diffcostFunc :: Fractional a => [(a, a)] -> (a, a) -> acostFunc ts d = scaleFactor * sum squares    where scaleFactor = 1.0 / (2 * fromIntegral (length ts))          squares = map (squareDiff d) ts-}