Problem with stored values, macro options, and interaction with other package

Apologies for the rather general title, while trying to sort out a very specific problem:
I'm trying to enhance the framework that Alan Munn provided here to represent naturally occurring discourse (with some extras, omitted here), with code from this answer by frougon. The point of it all is to have a mechanism for Alan's code (which uses the expex package) to not print repeated labels of speakers. I thought I would be able to integrate the two solutions, yet, with increased complexity, somehow along the way I managed to screw it up and for the life of me I can't figure out what's wrong.

This is as minimal a WE as it gets:

% !TEX TS-program = xelatexmk
\RequirePackage{filecontents}

\begin{filecontents}{discourse.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{discourse}

\RequirePackage[user,savepos]{zref}
\RequirePackage{expex}
\newcounter{linenum}
\newlength{\largestspkr}
\newlength{\largestnum}
\newcommand{\deflargestlabel}[2][99]{
\settowidth{\largestnum}{#1}
\settowidth{\largestspkr}{~#2}}
\deflargestlabel[999]{speaker~99}

\RequirePackage{perpage}

\newcounter{spkrcounter}
\MakePerPage{spkrcounter}

\newenvironment{discourseenv}{%
\setcounter{linenum}{0}%
\setcounter{spkrcounter}{0}%
\ignorespaces
}{%
\par\ignorespacesafterend
}

\DeclareOption{skiprepetitions}{%
\AtBeginDocument{\let\discourse@print\discourse@print@skip}%
}
\DeclareOption{keeprepetitions}{%
\AtBeginDocument{\let\discourse@print\discourse@print@keep}%
}
\ExecuteOptions{skiprepetitions}
\ProcessOptions\relax

\RequirePackage{xifthen}

\def\discourse@storedval{} % create a macro to later store a value in

\newcommand{\spkr}[1]{%
% create line numbers:
\refstepcounter{linenum}\makebox[\largestnum][r]{\thelinenum}\hspace{1em}
\ifthenelse{\isempty{#1}}%
{\makebox[\largestspkr][l]{}}% if empty > empty box
{% If this is the first \stepcounter{spkrcounter} executed since the current
% page was started, this sets 'spkrcounter' to 1.
\stepcounter{spkrcounter}%
\discourse@print{#1}%
\def\discourse@storedval{#1}%
}%
}

\def\discourse@print@skip#1{%
\ifthenelse{\cnttest{\value{spkrcounter}}>{1}\AND
\equal{#1}{\discourse@storedval}}%
{\makebox[\largestspkr][l]{}}% if repeated > empty box
{\makebox[\largestspkr][l]{#1:}}%
}
\def\discourse@print@keep#1{\makebox[\largestspkr][l]{#1:}}

\end{filecontents}

\documentclass{article}
\usepackage{discourse}

\begin{document}

Here it works:

\spkr{A} OK

\spkr{A} OK

\spkr{B} OK

\spkr{A} OK

\vbox{}
Here too in the environment:

\begin{discourseenv}

\spkr{A} OK

\spkr{A} OK

\spkr{B} OK

\end{discourseenv}

\vbox{}
But in combination with expex it screws up:

\begin{discourseenv}

\ex[exno=\spkr{A}, exnoformat=X] OK (prints A) \xe

\ex[exno=\spkr{A}, exnoformat=X] OK (empty) \xe

\ex[exno=\spkr{B}, exnoformat=X] OK (prints B) \xe

\ex[exno=\spkr{A}, exnoformat=X] OK (empty) \xe

\ex[exno=\spkr{B}, exnoformat=X] OK (prints B) \xe

\end{discourseenv}

\begin{discourseenv}

\ex[exno=\spkr{B}, exnoformat=X] OK (prints B) \xe

\newpage

\ex[exno=\spkr{B}, exnoformat=X] OK (prints B) \xe

\ex[exno=\spkr{B}, exnoformat=X] This one should be empty \xe

\ex[exno=\spkr{B}, exnoformat=X] This one should be empty \xe

\ex[exno=\spkr{A}, exnoformat=X] This one should print A  \xe

\ex[exno=\spkr{A}, exnoformat=X] OK (empty) \xe

\end{discourseenv}

\end{document}


It works as expected on the first couple of instances of the \spkr macro, but on the next page it behaves weirdly. Anyone able to spot what's wrong?

You just need to make the definition global, because \ex ... \xe is performed inside a group.

\newcommand{\spkr}[1]{%
% create line numbers:
\refstepcounter{linenum}\makebox[\largestnum][r]{\thelinenum}\hspace{1em}
\ifthenelse{\isempty{#1}}%
{\makebox[\largestspkr][l]{}}% if empty > empty box
{% If this is the first \stepcounter{spkrcounter} executed since the current
% page was started, this sets 'spkrcounter' to 1.
\stepcounter{spkrcounter}%
\discourse@print{#1}%
\gdef\discourse@storedval{#1}% <--- global!!!
}%
}


• Awesome thank you!! Quick question: That only applies to where I update the stored value? To define it first \gdef\discourse@storedval{} would be unnecessary? – jan Apr 11 at 8:12
• @jan You could do it for uniformity, but since the macro gets first defined at the top level, there's no problem in using \def. – egreg Apr 11 at 8:45