Checking for Constructors

When writing tactics, there are situations where it would be useful to know whether a term begins with a constructor. One possibility is to extract the head of the term and use the is_constructor tactic.



Ltac head t :=

match t with

| ? t' _ => head t'

| _ => t

end .



Ltac head_constructor t :=

let t' := head t in is_constructor t' .



Goal True .



head_constructor 0.

head_constructor 1.



Fail head_constructor (1 + 1).



Abort .





As of version 8.8, the is_constructor tactic is undocumented, but this should hopefully be fixed soon. Interestingly, we can achieve almost the same effect without is_constructor , by observing that Coq does not reduce a fixpoint unless it is applied to a term that begins with a constructor.



Reset head_constructor .

Ltac head_constructor t :=

match type of t with

| ? T =>

let r := eval cbn in (( fix loop ( t' : T ) { struct t' } := tt ) t ) in

match r with tt => idtac end

end .



Goal True .



head_constructor 0.

head_constructor 1.

Abort .





Unlike the previous version, this one reduces the term using cbn before testing it. Thus, the following test succeeds, while the analogous one before failed:



Goal True .



head_constructor (1 + 1).

Abort .





Update Anton Trunov pointed out that we can emulate the behavior of the first tactic with a fixpoint by adding a flag in the call to eval :



Reset head_constructor .

Ltac head_constructor t :=

match type of t with

| ? T =>

let r := eval cbn fix in (( fix loop ( t' : T ) { struct t' } := tt ) t ) in

match r with tt => idtac end

end .





Now, only the outer fixpoint is reduced.



Goal True .

Fail head_constructor (1 + 1).

Abort .



Please enable JavaScript to view the comments powered by Disqus.

Disqus