Best practices for debugging Motoko?

Hi,

In the rare occasions I have to read or write Motoko, I find it tricky, mainly because of having to deploy, making development iterations slow - probably there are better ways around this, as I found out about Haskell (it’s not a language I have knowledge) to write unit-tests, or the Motoko Playground.

What could help is to find how to use the mo:base/Debug to output any custom types, for example?

Any tips are appreciated,

Thanks!

1 Like

You can use debug_show(...) which turns any value into Text. Then you can use Debug.print(..) to print the text to console. But you need to deploy your canister locally and call the method in order to run it.

For faster iteration, you can invoke the motoko compiler moc directly. For example, $(dfx cache show)/moc should give you the command.

4 Likes

If using the playground, you can also log messages by appending them to a mutable variable of type Text and providing a query to read that variable.

Not great, but not terrible either.

3 Likes

GitHub - kritzcreek/motoko-matchers is pretty good for writing unit-tests of mainly libraries but also canisters (from dfx), but may have bit-rotted by now.

Some of the motoko-base libraries have their tests written with motoko-matchers.

1 Like

Thanks! The moc is extremely useful, using it now from now on :wink:

That’s a great idea, thank you so much for that!

Yes, PaulLiu, the docs suggest some degree of usefulness for Debug.print + debug_show.
And so in my youthful credulity I expected to enjoy the poetic value of the output of:

  public query func get1(): async Nat {
    Debug.print("I AM GOING TO RETURN 1 now");
    1;
  };

by running

dfx canister call bmos get1

How very disappointing was the output of (1 : nat) only!

Not very surprisingly by now, I fail to see how reading

BMOS.mo:1.1-1.34: import error [M0010], package "base" not defined

as a result of

$(dfx cache show)/moc BMOS.mo

can provide for faster iteration.

Would you please point out where my misunderstanding lies?

Thanks a lot in advance,

André

The debug output only shows up with the replica logs, i.e. in the terminal where you did dfx start or dfx start --background. I know, this is very counter-intuitive. Definitely something to improve.

Yes, you can run it like that after supplying the base library path to moc. For example:

moc --package base $(dfx cache show)/base -r BMOS.mo

The -r option would run the program instead of compiling it. Or you can use -i which will launch into an interactive shell where you can evaluate motoko expressions.

1 Like

Thanks for your quick reply, PaulLiu!

Well, guess I should have realized it myself: WHICH stdout are we talking about, right?
Certainly, I’ll start the replica in the foreground.

But may I make some more use of your helpfulness?
I started adding text to a var _log:Text = “”; and then “looking” at it by means of a query getter.
I know text gets added as expected, but my getter always shows an empty string;
Are variables inside the actor re-initialized / re-evaluated with every invocation?!

1 Like

I think this isn’t working for you because your function is itself a query so it’s state changes are discarded before returning, including the updates to your Text variable.