Implementing this OO concept in Haskell

I need help translating the concept of OO into Haskell.

Imagine class Vehicleand subclasses Car, and Truckby a method driveOneMile, which returns the binary representation the total fuel used. Each call driveOneMilealso changes the internal state of the vehicle.

This is what I have done so far in Haskell (since there are no instance variables in Haskell, it seems to me that I should create my own state types):

type CarState = (Double,Double)
initialCarState = (0,0)

driveCarOneMile :: CarState -> (Double,CarState) --Double: total fuel used
driveCarOneMile s = ...


--the internal state of trucks is more complex. needs three Doubles
type TruckState = (Double,Double,Double)
initialTruckState = (0,0,0)

driveTruckOneMile :: TruckState -> (Double,TruckState)
driveTruckOneMile s = ...

In a similar way, I could build other vehicles and their “drive” functions.

Here's how I drove a car twice:

fuelFor2Miles = fst $ driveCarOneMile $ snd $ driveCarOneMile initialCarState

  • Am I doing it right?
  • If not, how do you fix it?
  • ( ), , , 10 [] ? ( OO , 10 .)
+4
4

Haskell OO, , OO, , . , "" , " OO, ( Haskell Antipattern: Existential Typeclass).

, , .

data Vehicle = Car CarState | Truck TruckState

driveOneMile :: Vehicle -> (Double, Vehicle)

, .

- (, , ), , . Vehicle , , , ( , ). Vehicle toGeneralVehicle :: a -> GeneralVehicle , GeneralVehicle.

P.S.: , TruckState -> (Double,TruckState), . , , State! State , . , State.

+8

, , , . , , .

duplode,

data Vehicle = Car CarState | Truck TruckState

driveOneMile

driveOneMile :: State Vehicle Double
driveOneMile = do
    vehicleState <- get
    case vehicleState of
         Car carState -> do
             ...
             return totalFuelUsed
         Truck truckState -> do
             ...
             return totalFuelUsed

, , , , ? , , .

driveTwoMile driveNMiles n :

driveTwoMile :: State Vehicle Double
driveTwoMile = do
    firstMileFuelUsage <- driveOneMile
    secondMileFuelUsage <- driveOneMile
    return (firstMileFuelUsage + secondMileFuelUsage)

driveNMiles :: Int -> State Vehicle Double
driveNMiles n = do
    fuelUsages <- replicateM n driveOneMile
    return (sum fuelUsages)

, , , ( ):

type DrivingDistance = Int

driveManyVehicles :: [Vehicle] -> [DrivingDistance] -> [(Double, Vehicle)]
driveManyVehicles v d = do
    (currentVehicle, drivingDistance) <- zip v d
    return (runState (driveNMiles drivingDistance) currentVehicle)
+2

, :

class Vehicle a where
  driveOneMile :: a -> Double

data Truck = Truck -- Fill in the details...
data Car   = Car   -- Fill in the details...

instance Vehicle Truck
instance Vehicle Car

, , .

0

:

I could not combine a car and a truck so that it fits one type of amount. Sum types are not intended to describe some generality of data between types (but rather, for an unrelated case pooling). You can fix the generality of data using the type class (although this does not allow you to save only two types). Then I would define your Vehicle collection as lists of each type.

type Miles = Double
type Gallons = Double

data Truck = Truck { tid :: Int, truckTankGallons :: Gallons, tmpg :: Double  } deriving (Show)
data Car   = Car   { cid :: Int, carTankGallons   :: Gallons, cmpg :: Double  } deriving (Show)

class GasVehicle a where
  mpg :: a -> Double
  gasGallons :: a -> Gallons
  changeGasAmt :: Gallons -> a -> a

instance GasVehicle Car where
  mpg = cmpg
  gasGallons = carTankGallons
  changeGasAmt g c = Car (cid c) g (cmpg c)

instance GasVehicle Truck where
  mpg = tmpg
  gasGallons = truckTankGallons
  changeGasAmt g c = Truck (tid c) g (tmpg c)

milesToGallons :: GasVehicle a => a -> Miles -> Gallons
milesToGallons a m = m / (mpg a)

driveMiles :: GasVehicle a => Miles -> a -> (a, Gallons)
driveMiles m a = (a', dg) where
  dg = (milesToGallons a m)
  a' = changeGasAmt (gasGallons a - dg) a

data Vehicles = Vehicles { cars :: [Car], trucks :: [Truck] } deriving (Show)

driveVehicles :: Miles -> Vehicles -> (Vehicles, Gallons)
driveVehicles m v = (Vehicles cars' trucks', gasUsed) where
  gasUsed = (sum carGallons) + (sum truckGallons)
  (cars', carGallons)     = unzip $ map (driveMiles m) $ cars v
  (trucks', truckGallons) = unzip $ map (driveMiles m) $ trucks v
0
source

Source: https://habr.com/ru/post/1539608/


All Articles