5
$\begingroup$

I'm soon going to introduce product types (C struct and the like in different languages). The audience is 17 year old with a long term goal in scientific studies, who have by now a very limited knowledge of programming, mainly only basic loops in C with integers and integer arrays.

I would like to motivate the need and usefulness of this concept, knowing that they're going to say "but we like to do everything with our basic ints". I would moreover like to propose examples that are interesting, realistic, and with a glimpse to theoretical aspects (I am going to tell them not too late that what we are doing is a cartesian product of the considered base types).

By now, my main idea is the classic points in the plane, maybe complex numbers. It's very good for my goals (there are reasons why it's a classic), but it has the only disadvantage that the two combined base types are the same. The risk is that, having them already been exposed to arrays, their first impression of struct is a needless complication when they could have easily used an array of length 2.

Of course there are all those "real (accountant) world" situations, where I put together a name and an age, or an item and a price and so on. But the students are in science education, I'm not particularly fond of accounting and administrative applications, and most of all I would like to avoid the consolidation of the view that computer science is a tool for accountants. Instead, I'd prefer something about science or computing.

There is also the obvious construction of a linked list in C, but it's too complicated for them: no pointers.

Put somehow differently, from another angle (their): why would I want to define a new type if I do not do software engineering? I can bring all my data in primitive and disaggregated form as I have done for years: the advantage of creating a new type is not worth the burden of having to learn things. Where even is this advantage again? (Writing this line I am remembering of blub language and how it's difficult to make these motivations even for experienced computer scientists, doing it for young students does not seem an easy thing, seen from here).

I'd like instead that they could be put in a situation where they want to construct new types.

Any suggestions?

(I have asked chatGPT too and it seems to be stuck in my same position).

$\endgroup$
7
  • 2
    $\begingroup$ There are lots of real world situations that do not involve accounting. Take examples from their personal lives, be it text messaging, campus management, sports or gaming entities. Take a look at introductions to OOP, they have exactly the same problem of realistic and interesting yet simple objects. $\endgroup$
    – Bergi
    Commented Feb 10 at 22:49
  • 1
    $\begingroup$ Frankly, structs are much more interesting and useful if you have pointers to play with. They are also going to need both pointers and structs if they're ever going to do any (nontrivial) graph theory. Are you absolutely sure that you can't teach pointers? $\endgroup$
    – Kevin
    Commented Feb 11 at 1:33
  • 1
    $\begingroup$ Why do you think they will reject the idea of using a struct, and instead insist on using arrays? Did this already happen to you before with other students? $\endgroup$ Commented Feb 12 at 13:53
  • $\begingroup$ @Kevin oh I know unfortunately well that the game is much better with pointers (let's say frankly that C without pointers is not C, and consequently also not useful). Unfortunately it's absolutely out of discussion here, the curriculum is what it is and I can't say much about it. Also the idea of using C as an introductory language is, in my view, not a good idea. $\endgroup$
    – user9137
    Commented Feb 13 at 10:36
  • 1
    $\begingroup$ @MatthieuM. Mmh, yes I know the students' ways of thinking quite a bit now and this is a common problem. Not certain of course, but surely something to be considered when planning the lessons. This is also one of the reasons why it's not a good idea for a curriculum to talk about lists (or arrays) before structs: the topics are quite cleary reversed on the path in pedagogical terms. Structs deal with still constant size data, lists/arrays are the introduction to arbitrarily sized data (see the wonderful HTDP book for a reference on a very well thought learning path for absolute beginners). $\endgroup$
    – user9137
    Commented Feb 13 at 10:45

4 Answers 4

6
$\begingroup$

The primary purpose of abstractions is to give names to collections of more basic ideas. Instead of writing, "I traveled by putting one foot in front of the other such that at least one foot was always on the ground," we instead write, "I walked." Not only is the second version shorter, but it unifies several discrete motions into a single action so that the reader can understand with less thought. The first sentence requires the reader to take multiple ideas ("traveled," "one foot in front of the other," "one foot always on the ground") and compose them in their head to arrive at the idea being communicated. Writing only in only the most basic ideas forces the reader to do more work. We want computers to do the work!

If your students have learned about functions, you can make this comparison:

  • A function is a collection of instructions that has been given a name.
  • A struct is a collection of data that has been given a name.

Since your class is science-oriented, let's make a catalogue of planets. Please forgive any language errors as C is not my primary language.

struct Planet
{
    char *name;
    double mass; /* kg */
    double length_of_year; /* Earth days */
    double distance_from_sun; /* km */
};

Even if the data fields are homogenous, you can still emphasize the importance of names. For example, let's consider Kepler's third law: the ratio of the square of a planet's year to the cube of the planet's distance from the sun is a constant. We can verify this law by printing the ratio for each planet.

#include <math.h>

Planet mercury;
mercury.name = "Mercury";
mercury.mass = 3.3e23;
mercury.length_of_year = 115.88;
mercury.distance_from_sun = 57.91e6;

/* all the other planets */

Planet solar_system[10] = {mercury, venus, earth, mars, jupiter, saturn, uranus, neptune, pluto, sedna};

for(int i = 0; i < 10; i++)
{
    Planet planet = solar_system[i];
    double kepler_ratio = pow(planet.length_of_year, 2)/pow(planet.distance_from_sun, 3);
    printf("%s: %e", planet.name, kepler_ratio);
}

Now imagine the same code using just double[3] to store the data of each planet.

double solar_system[10][3] = {{3.3e23, 115.88, 57.91e6}, /* etc. */};
char *planet_names[10] = {"Mercury", "Venus", /* etc. */};

for(int i = 0; i < 10; i++)
{
    double planet_data[3] = solar_system[i];
    double kepler_ratio = pow(planet_data[1], 2)/pow(planet_data[2], 3);
    printf("%s: %e", planet_name[i], kepler_ratio);
}

You can ask your class,

  • Are we sure we got the subscripts right?
  • Are the names of the planets matched up with the data in the other array?
  • What happens when we realize that the planet masses aren't used and delete that data?
  • How much time to you want to spend counting indices to get the right data in the right place when we have a computer sitting right here that can count for us?

Sure, you could read the comments (if there are any and they are correct) on the arrays to see what each index means. But, the first version is easier to read. Why is it easier to read? We are using names that humans understand. Instead of interpreting code, the reader is interpreting human language. In the first version with the struct, we only have to check that the code correctly implements Kepler's third law.

We humans have problems we want to solve, and computers are one tool that can help. As much as possible, we want to express our problems in human terms that we can understand. The only thing a computer understands is voltages on wires (even 1 and 0 are symbolic abstractions of electrical activity). The purpose of a compiler/interpreter is to automatically translate human concepts into machine activity. The closer that the code we write approaches human language and ideas, the easier it is to understand and debug.

$\endgroup$
6
  • $\begingroup$ Playing Devil's Advocate: You could use N arrays of 1 element instead of 1 array of arrays of N elements (problem => introducing/removing an element is a pain). You could use named constants for the indexes (problem => what if the index varies depending on which array is used?). $\endgroup$ Commented Feb 12 at 13:51
  • $\begingroup$ @MatthieuM. That's actually the way I work in my job. Some equipment dumps out timestamped data to a csv file, and I analyze it with arrays, one for each column. This is less of a pain that I implied in my answer since, luckily, the python Numpy library lets me treat each column of data as a single variable since most mathematical operations are implicitly element-by-element, removing the need for for-loops. Then again, there's no real cohesion in the data in each row other than being measured in the same moment. There's not much gained with a Data struct. $\endgroup$
    – Mark H
    Commented Feb 13 at 6:16
  • $\begingroup$ Well... in your case it's easy because you're talking about an immutable "struct of array". Now, try to sort those "virtual" struct based on the lexicographical order of the tuple of the first two "virtual" fields. Good luck ;) $\endgroup$ Commented Feb 13 at 8:06
  • $\begingroup$ This!! Thanks! Great answer, it deals precisely and accurately with the important points. $\endgroup$
    – user9137
    Commented Feb 13 at 11:10
  • $\begingroup$ @MatthieuM. numpy.lexsort: Get an array of indices that sorts according to the criteria, then use those indices to sort all the other columns in the same way. :) $\endgroup$
    – Mark H
    Commented Feb 13 at 13:47
7
$\begingroup$

First thing first, a C function may not return an array (without some hoops jumping). It may however return a struct, fair and square: struct is a value, array is not.

Structural values arise naturally in many contexts. Unfortunately, most of them are recursive, which you rule out. The non-recursive example - off the top of my head - would be tokenizing a programming language, where a token is comprised of a type, and a value (and a type of value depends on type). I don't know if you consider it too complicated for your students.

Now to the real point. Structs is not about Cartesian products. They are not about the computational power. They are about the expressiveness. Reconsider complex numbers.

void add(double z1[2], double z2[2], double result[2]);

vs

struct complex {
    double re;
    double im;
}

struct complex add(complex z1, complex z2);

Which one is more expressive? Which one is less error-prone?


Yes, you could do everything with the basic ints, but why should you?

$\endgroup$
8
  • $\begingroup$ Upvoted. The first time I heard someone talk about "product types" I thought, why are you using pseudo-mathematical vocabulary to talk about such a practical concept? Why are you trying to hide behind these big words? $\endgroup$
    – Stef
    Commented Feb 11 at 16:34
  • $\begingroup$ @Stef the word "produt type" only really makes sense if you also introduce the word "sum type" (introductory example) $\endgroup$
    – mb21
    Commented Feb 12 at 15:57
  • $\begingroup$ Yes, precisely, this is more or less the point to look for good examples on the theme. The fact that structs will allow us to "return something" will be the crucial point in trying to make them appreciate the idea: it's more or less the way through which thay can start to understand what we mean by "expressiveness". If I just tell them "we want more expressiveness" they will look at me like crazy: they have absolutely no ground to base this term. Yes, structs are a tool for expressiveness. $\endgroup$
    – user9137
    Commented Feb 13 at 10:59
  • $\begingroup$ @Stef it's not "pseudo-mathematical", it's "computer science". All the concepts that we use have a meaning, when a concept survive through a lot of evolution is because it is useful, and the usefulness of a concept is based also on its practicality. I am not hiding anything, nor know why should you think so. The "conceptual" terms serve their goals of being clear, concise, well known. Moreover, the students would not be bad served by using a good name: the cartesian product carries the same name, it is well linked to the concept, and is the only thing already familiar to the students. $\endgroup$
    – user9137
    Commented Feb 13 at 11:07
  • $\begingroup$ Oh I forgot to mention that I like the tokenizing / language context, thanks. $\endgroup$
    – user9137
    Commented Feb 13 at 11:14
5
$\begingroup$

I have a few thoughts for you. First, a useful context to consider is Machine Learning, which is both scientific, and regularly involves mixed data types. Your administrative examples could pretty easily be recast to machine learning examples with just a tiny bit of massaging. Mathematical examples tend purely towards numbers, so might pose exactly the motivational difficulty for the topic that you've fingered.

That said, a simple idea for a first motivating example is a standard (x,y) plane point, but with a label. So, place point A at (0,10) and point B at (10,-10). Heck, create 26 points, one for each letter of the alphabet, at random places. Or any largish number of points, arranged however you'd wish.

Then calculate the midpoints. Since there are a lot of points, you'll naturally want a function.

The need for the structure comes as so: you can't return something like [label:"midpoint between A and B",x:5,y:-5] as an array, and the number of points would make the results impractical to interpret without the labels. Hence, we really need the struct.

If you really want to dig into the example, you could try doing it without the struct first, and the kids will see how difficult it becomes before the struct simplifies everything.

$\endgroup$
1
  • $\begingroup$ Thanks! These are really interesting starting points. I hadn't thought about ML because I don't know much about it, I'll try to search for something. $\endgroup$
    – user9137
    Commented Feb 10 at 18:27
2
$\begingroup$

The other answers suggest using struct to implement complex coordinates. While these may be familiar to students with a scientific background, they suffer from the fact that they're homogeneous, so could easily be implemented as int complexnum[2];.

What makes structures interesting is that they collect heterogeneous types, so you should use that kind of example to motivate. While it's not science-related, a simple phone directory will be something they can relate to.

struct contact {
    char name[100];
    char address[100];
    char city[20];
    char state[200];
    char zip[10];
    char phone[20];
};

If you want something more scientitic, make this a catalog of stars. This will contain the name (a string), magnitude (float), astronomical coordinates (two floats).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.