HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Ricochet Challenge: Robust IO and working with different types of numbers in Haskell

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
robustchallengewithnumbersworkingdifferentricochethaskelltypesand

Problem

Background

I've tried to solve /r/DailyProgrammer Challenge #303 [Easy]: Ricochet (summarised below) in Haskell. The code below is now the second Haskell script I've ever written, so I'd consider myself quite the beginner. It works (to a bare minimum), but I'm not particularly happy with it.

My knowledge in Haskell at this point spans as far as the first two chapters of the fantastic Haskell Wikibook. I've not reached functors, monads, or friends, so if the solutions to my queries lie there then it might be wisest for me to settle until I learn more.

My Queries

Whilst I'm sure there are many problems, my main concerns in the code below regard the manner in which I'm dealing with the numerical arguments to ricochet, and the fragile nature of IO.

More particularly, regarding ricochet:

  • I wanted the 5 arguments (namely h, w, m, n and v) all to be any kind of integer (not minding Int or Integer, etc., so long as it's a whole number, mathematically speaking), barring v which can be any number at all (i.e. 1.05).



  • Whilst need for integers is important (as modulo (and thus the algorithm) wouldn't make sense without them), I also want to be able to perform arbitrary arithmetic, including any kind of division.



  • I took me much time and frustration to come up with anything that'd even compile, for what's considerably basic arithmetic, and at the end I still find that function signature and spam fromIntegral nasty. Any advice on dealing with this kind of situation in future?



And regarding IO:

  • What's there now is the bare minimum to work with the challenge. I'm not happy with it because it's so fragile. If anything at all is wrong with the input, the whole program explodes.



  • I'd rather just output a blank line if the input is invalid, but I've no idea how to get that done.



However, I'd of course appreciate any advice at all, even around spacing/indentation!

```
#!/usr/bin/env stack
-- stack --resolver=lts-8.2 --install-ghc runghc

{-
Rico

Solution

You can reduce the fromIntegral spam like this:

ricochet :: (Integral a, Fractional b) => a -> a -> a -> a -> b -> (Corner, a, b)
ricochet h w m n v
    = (c,b,fromIntegral d / v)

    where
    h' = h - m
    w' = w - n
    d  = lcm w' h'


The return value of functions like - and lcm is of the same type as their arguments, so the types of h' and the like can be inferred. You do need to write fromIntegral d / v because the compiler doesn't perform automatically the conversion required for the division operator. It's frequent to have these conversions functions at "type cast" boundaries.

But what if you want to keep the explicit signatures, for clarity? You can use "explicit forall" along with the ScopedTypeVariables extension, which allow you to say "this type here is the exact same type as the type in the top-level signature".

{-# LANGUAGE ScopedTypeVariables #-}
module Main
    ( Corner(..)
    , ricochet
    , main
    ) where

data Corner = UL | UR | LR | LL deriving (Show, Eq)

ricochet :: forall a b. (Integral a, Fractional b) 
         => a -> a -> a -> a -> b -> (Corner, a, b)
ricochet h w m n v
    = (c,b,fromIntegral d / v)

    where
    h', w', d :: a
    h' = h - m
    w' = w - n
    d  = lcm w' h'

Code Snippets

ricochet :: (Integral a, Fractional b) => a -> a -> a -> a -> b -> (Corner, a, b)
ricochet h w m n v
    = (c,b,fromIntegral d / v)

    where
    h' = h - m
    w' = w - n
    d  = lcm w' h'
{-# LANGUAGE ScopedTypeVariables #-}
module Main
    ( Corner(..)
    , ricochet
    , main
    ) where

data Corner = UL | UR | LR | LL deriving (Show, Eq)

ricochet :: forall a b. (Integral a, Fractional b) 
         => a -> a -> a -> a -> b -> (Corner, a, b)
ricochet h w m n v
    = (c,b,fromIntegral d / v)

    where
    h', w', d :: a
    h' = h - m
    w' = w - n
    d  = lcm w' h'

Context

StackExchange Code Review Q#156273, answer score: 3

Revisions (0)

No revisions yet.