3

I want to create partial functions that enable values of the same unit and dimension to be added and multiplied, so far I've got the following type definitions, I also need to include units conversion. This allows adding values of the same unit but with different dimensions. I don't need all eventualities, just those that are compatible. For example:

 datatype temp_dimension = Celsius | Fahrenheit; 
 datatype dist_dimension = Meters | Centimeters | Kilometers; 
 datatype units = Temp of temp_dimension | Distance of dist_dimension;
 type value = (real * units);

Example use:

add ((3.5, Temp (Celsius)), (4.5, Temp (Celsius)))
=> (8.0, Temp (Celsius))
mul ((2.0, Distance (Meters)), (3.0, Distance (Meters)))
=> (6.0, Distance (Meters))
add ((2.0, Distance (Meters)), (3.0, Distance (Centimetres)))
=> (2.03, Distance (Meters))

I have tried this code:

fun add (x1, y1) (x2, y2) = 
  ((x1 + x2) + y2)) add Temp(Celsius); 

fun mul (x1, y1) (x2, y2) = 
   ((x1 * x2) + y2);
ben rudgers
  • 3,647
  • 2
  • 20
  • 32
  • It's not really clear what problem you are trying to solve. Can you give some examples of the input and output you are trying to achieve? – ben rudgers Apr 06 '15 at 19:17
  • Using the followingdefinitions, create partial functions that enable values of the same unit and dimension to be added and multiplied. datatype temp_dimension = Celsius | Fahrenheit; datatype dist_dimension = Meters | Centimeters | Kilometers; datatype units = Temp of temp_dimension | Distance of dist_dimension; type value = (real * units); You don’t need to consider all eventualities, just those that are compatible. So be able to: add ((3.5, Temp (Celsius)), (4.5, Temp (Celsius))) => (8.0, Temp (Celsius)) mul ((2.0, Distance (Meters)), (3.0, Distance (Meters))) => (6.0, Distance (Meters)) 1 – user3330146 Apr 06 '15 at 19:40
  • Sorry for not being clear, that's the information I got :) – user3330146 Apr 06 '15 at 19:43
  • It's not really clear what you are asking. What code have you tried? – ben rudgers Apr 06 '15 at 21:04
  • I have tried the following function: fun add (x1, y1) (x2, y2) = ((x1 + x2) + y2)) add Temp(Celsius); fun mul (x1, y1) (x2, y2) = ((x1 * x2) + y2); But I'm not sure wether how to start. I don't have any more information than this: "Task 1: Using the following type definitions, create partial functions that enable values of the same unit and dimension to be added and multiplied." – user3330146 Apr 06 '15 at 21:20
  • My recommendation: Start by just doing the maths in separate functions without worrying about the units and datatypes. After that is working, make some unit conversion functions. Then build a function that does case analysis on the unit types as the last piece. – ben rudgers Apr 09 '15 at 19:00
  • Incidentally, note that what you're trying to do doesn't make sense, for the multiplication case. The product of 3 meters and 5 meters is not 15 meters, but rather 15 meters squared; and there's no reason at all to forbid quantities with different dimensions and/or units from being multiplied. (I mean, from a programming standpoint, this doesn't matter -- it's all arbitrary -- but it doesn't make sense from the standpoint of modeling anything outside your program.) – ruakh Apr 10 '15 at 23:51

2 Answers2

1

A partial function is a function which is not defined for all possible inputs. In your case, neither add nor mul are defined when trying to call them with units of incompatible types. For example, if you'd try to add Celsius and Meters values. In that case, the only reasonable approach would be to throw an exception.

So, here's a skeleton to get you started:

fun add ((v1, Temp t1), (v2, Temp t2)) = addTemp ((v1, t1), (v2, t2))
  | add ((v1, Distance d1), (v2, Distance d2)) = addDist ((v1, d1), (v2, d2))
  | add _ = raise Fail "incompatible units in addition"

fun mul ((v1, Temp t1), (v2, Temp t2)) = mulTemp ((v1, t1), (v2, t2))
  | mul ((v1, Distance d1), (v2, Distance d2)) = mulDist ((v1, d1), (v2, d2))
  | mul _ = raise Fail "incompatible units in multiplication"
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • Hi Stan, I posted a reply below since I couldn't fit the characters. What do you think so far? – user3330146 Apr 08 '15 at 13:56
  • @user3330146 you really should read a Standard ML tutorial if you're actually interested in the language. You can't write a program without understanding the basic syntax of the language, at least. I can't help you more than that. – Ionuț G. Stan Apr 08 '15 at 16:43
  • In fairness, there aren't really good beginner friendly resources for SML unless they come straight from someone like a course instructor. – ben rudgers Apr 09 '15 at 18:26
  • @benrudgers ok, that's a fair point. Material on Standard ML is scarce, but not nonexistent. I recommend this book http://www.cs.cornell.edu/riccardo/prog-smlnj/notes-011001.pdf and this course http://www.cs.cornell.edu/courses/cs312/2007sp/schedule.html – Ionuț G. Stan Apr 09 '15 at 18:37
  • Sure, there are things out there. And there's nothing wrong with old established knowledge locked up in PDF's, in my opinion. Yet, in today's world any computing language tutorial published during the Clinton Administration to a first approximation is obsolete. SML is a long tail outlier and a 200+ page TEX based PDF is a big ask for a simple homework-like problem. It's not an easy pedagogical challenge to overcome. – ben rudgers Apr 09 '15 at 18:46
  • @benrudgers I mostly agree, but can't do anything about it right here on StackOverflow. When I have/want to learn something new, I have to invest time and I kind of expect the same from others. A shorter resource for getting acquainted with SML might be this: http://learnxinyminutes.com/docs/standard-ml/ Let's hope the OP sees them. – Ionuț G. Stan Apr 09 '15 at 18:55
  • Yes, neither of us is able to fix the internet today. – ben rudgers Apr 09 '15 at 19:00
0

The key to organizing the code is to understand that this:

datatype temp_dimension = Celsius | Fahrenheit
datatype dist_dimension = Meters | Centimeters | Kilometers
datatype units = Temp of temp_dimension | Dist of dist_dimension

Strongly suggests that the code should dispatch on type.

Add temp_dimension's

Since temp_dimension only has two subtypes, its structure is a bit simpler. On the other hand, converting between the subtypes is more complicated than for dist_dimension and warrants some testing. That's what makes the world interesting. To add temp_dimensions the code has to handle four cases:

fun add_temp ((v1 : real, t1 : temp_dimension),
          (v2 : real, t2 : temp_dimension)) =
  let
      val freeze = 32.0
      val temp_ratio = (5.0 / 9.0)
      fun cel2fahr (c : real) = (c / temp_ratio) + freeze
      fun fahr2cel (f : real) = (f - freeze) * temp_ratio
  in
    case t1 of
    Celsius => (
      case t2 of
          Celsius => v1 + v2
        | Fahrenheit => v1 + fahr2cel(v2)
          )
  | Fahrenheit => (
      case t2 of
          Fahrenheit => v1 + v2
        | Celsius =>  v1 + cel2fahr(v2)
          )
  end

Add dist_dimension's

While there are nine cases for an operation on dist_dimensions:

fun add_dist ((v1 : real, t1 : dist_dimension),
          (v2 : real, t2 : dist_dimension)) =
  let
      fun cent2meter (c : real) = c * 100.0
      fun cent2kilometer (c : real) = c * 100000.0
      fun meter2kilometer (m : real) = m * 1000.0
      fun meter2cent (m : real) = m / 100.0
      fun kilometer2cent (k : real) = k / 100000.0
      fun kilometer2meter (k : real) = k / 1000.0
  in
      case t1 of
      Centimeters => (
       case t2 of
           Centimeters => v1 + v2
         | Meters => v1 + meter2cent v2
         | Kilometers => v1 + kilometer2cent v2
           )
       | Meters => (
       case t2 of
           Centimeters => v1 + cent2meter v2
         | Meters => v1 + v2
         | Kilometers => v1 + kilometer2meter v2
           ) 
       | Kilometers => (
       case t2 of
           Centimeters => v1 + cent2kilometer v2
         | Meters => v1 + meter2kilometer v2
         | Kilometers => v1 + v2
           )
  end

Add

The add function dispatches on the type units as Ionut G. Stan suggested:

fun add ((v1, Temp t1), (v2, Temp t2)) = add_temp((v1, t1),(v2, t2))
  | add ((v1, Dist t1), (v2, Dist t2)) = add_dist ((v1, t1),(v2, t2))
  | add _ = raise Fail "Incompatible units cannot be added"

Comments

I find it easier to think clearly when I am explicit about types in function declarations. For example, it is easy to get tripped up on integer versus real in a language with strong static typing like SML.

I also don't worry about being brief. Correct is easier than correct + code golf.

Finally, there are a number of higher level functions that could be written to handle the repetitive parts of this sort of program. It's fun to think about them, but they don't make for the clearest of answers.

ben rudgers
  • 3,647
  • 2
  • 20
  • 32