Home > other >  How can we pull function definitions into a type class, instead of a reference
How can we pull function definitions into a type class, instead of a reference

Time:06-07

I have provided some code below that demonstrates the basic concept of a project. I have modules that are set up as interfaces; I implement the interfaces to build modules. In the example below, I built an Alpha.

type Ticker = String
type Shares = Int
type Price = Float
data Insight = Down | Flat | Up deriving (Show, Eq, Ord)
type Target = Float

data Universe = Universe {generateUniverse :: [(Ticker, Price)] -> [(Ticker, Price)]}
data Alpha = Alpha {generateInsights :: [(Ticker, Price)] -> [(Ticker, Insight)]}
data Portfolio = Portfolio {generateTargets :: [(Ticker, Insight)] -> [(Ticker, Target)]}
data Execution = Execution {generateOrders :: [(Ticker, Price)] -> [(Ticker, Target)] -> [(Ticker, Shares)]}

convert :: (Ticker, Price) -> (Ticker, Insight)
convert (t, p)
  | p < 500 = (t, Down)
  | p == 500 = (t, Flat)
  | p > 500 = (t, Up)

split :: [(Ticker, Price)] -> [(Ticker, Insight)]
split xs = foldr (\tp acc -> (convert tp):acc) [] xs

splitAlpha :: Alpha
splitAlpha = Alpha {
  generateInsights = split
  }

main :: IO ()
main = do
  let
    alpha = splitAlpha
  print (generateInsights alpha [("TSLA", 500.0), ("RKT", 10.0), ("AMC", 750)])

How can I compress my definition of splitAlpha so that there is not as much nesting in the definition of generateInsights? I have attempted the example below...

convert :: (Ticker, Price) -> (Ticker, Insight)
convert (t, p)
  | p < 500 = (t, Down)
  | p == 500 = (t, Flat)
  | p > 500 = (t, Up)

splitAlpha :: Alpha
splitAlpha = Alpha {
  generateInsights xs = foldr (\tp acc -> (convert tp):acc) [] xs
  }

and recieved this error:

ghci> :cmd return $ unlines [":l itk", ":main"]
[1 of 1] Compiling Main             ( itk.hs, interpreted )

itk.hs:23:20: error: parse error on input `xs'
   |
23 |   generateInsights xs = foldr (\tp acc -> (convert tp):acc) [] xs
   |                    ^^
Failed, no modules loaded.

<interactive>:60:53: error:
    * Variable not in scope: main :: IO a0
    * Perhaps you meant `min' (imported from Prelude)

CodePudding user response:

You can work with a lambda expression, so:

splitAlpha :: Alpha
splitAlpha = Alpha {
  generateInsights = \xs -> foldr (\tp acc -> (convert tp):acc) [] xs
  }

In this specific case however, this is just a mapping function, so you can work with:

splitAlpha :: Alpha
splitAlpha = Alpha {
  generateInsights = map convert
  }

CodePudding user response:

As Willem Van Onsem wrote, this example is super easy because the whole thing boils down to generateInsights = map convert. But more generally, it wouldn't be so easy. Lambda syntax only works for single-clause functions with no guards

splitAlpha = Alpha
  { generateInsights = \xs -> ...
  }

More generally, you can always use let to have a proper definition-scope in which you can define any function locally with full syntax available, but avoiding to populate any other namespace:

splitAlpha = Alpha
  { generateInsights
       = let gi xs = foldr (\tp acc -> (convert tp):acc) [] xs
         in gi
  }
  • Related